//Path: ISPD_Contest/sizer-v1.0

//#include "coneSampling.h"
#include "../include/optimization.h"
#include "../include/timer_interface.h"
#include "../include/utility.h"
#include <algorithm>
#include <stack>
#include <set>
#include "time.h"
#include "pthread.h"
#include <ctime>
#include <assert.h>
//#include "/opt/ILOG/CPLEX_Studio_AcademicResearch122/cplex/include/ilcplex/ilocplex.h"
#include <ilcplex/ilocplex.h>

#include <boost/random/uniform_int.hpp>
#include <boost/random/mersenne_twister.hpp>

#include "../src/validate.cpp"
//#include "../include/mip.h"
#include "../include/parser_helper.h"
#include "../include/extendedlibparser.h"
#include "../include/extendedtimingparser.h"
#include "../include/extendedspefparser.h"
#include "../include/extendedsdcparser.h"
#include "../include/extendedverilogparser.h"
#include "../include/circuitgraph.hpp"
#include "../include/extendedverilogparser.h"

//global variables, Min
CircuitGraph* graphInPtr;
ExtendedVerilogParser* vpPtr;
ExtendedSdcParser* sdcPtr;
ExtendedLibParser* lpPtr;
ExtendedTimingParser* tpPtr;
//vector<string> optList;
unsigned int largestNumOption;
unsigned long effGateCnt;
vector<string> sizeVec;
long opt_rd;//optimization rounds
typedef boost::mt19937 gen;

using namespace std ;

typedef struct vector_{
        double x;
        double y;
        double z;
} vec;

typedef struct window_{
        int x_width;
        int y_width;
        pair<int, int> startpt;
} window;

#ifndef REDUCED_OPTIONS
#define REDUCED_OPTIONS
#endif

#ifndef RELAX_DELAY
//#define RELAX_DELAY
#endif

#ifndef MORE_PLANE
//#define MORE_PLANE
#endif

#ifndef ENA_PPRO
//#define ENA_PPRO
#endif

#ifndef LIN_RELAX
//#define LIN_RELAX
#endif

//should be on for both cases
#ifndef ZOOM_IN
#define ZOOM_IN
#endif

//zoom in comes with radius based
#ifndef RADIUS_BASED
#define RADIUS_BASED
#endif

#ifndef RANDOM_PLANE               
#define RANDOM_PLANE                
#endif

ILOSTLBEGIN
typedef IloArray<IloNumArray> NumMatrix;
typedef IloArray<IloNumVarArray> NumVarMatrix;
void calCrossproduct(vec &v1, vec &v2, vec &nv){
        nv.x = v1.y * v2.z - v1.z * v2.y;
        nv.y = v1.z * v2.x - v1.x * v2.z;
        nv.z = v1.x * v2.y - v1.y * v2.x;
}


window lutZoomin(LibParserCellInfo &cell, string instName, string &fromPin, double loadCap, bool &valid){
        
        int x, y;
        window w_0;
        w_0.x_width = 0;
        w_0.y_width = 0;
        w_0.startpt.first = 0;
        w_0.startpt.second = 0;
        //const int radius = 4;
        int x_width = 6 - opt_rd;
        int y_width = 7 - opt_rd;

        if(x_width <= 0)
                x_width = 1;
        if(y_width <= 0)
                y_width = 1;
        
        const int x_half_floor = floor((double)x_width / 2.0);
        const int x_half_ceil = ceil((double)x_width / 2.0);
        const int y_half_floor = floor((double)y_width / 2.0);
        const int y_half_ceil = ceil((double)y_width / 2.0);
        
        vector<LibParserTimingInfo>::iterator it_lbpt;
        string tmp = instName;
        tmp += "/" + fromPin;
        //cout << tmp << endl;
        double slew = max(tpPtr->PinsTiming[tmp].riseTransition, tpPtr->PinsTiming[tmp].fallTransition);
        
        for(it_lbpt = cell.timingArcs.begin(); it_lbpt != cell.timingArcs.end(); it_lbpt++)
                if(it_lbpt->fromPin == fromPin)
                        break;
        
        assert(it_lbpt != cell.timingArcs.end());
        
        for(x = 0; x < 7; x++){
                if(it_lbpt->fallDelay.loadIndices[x] >= loadCap)
                        break;
        }
        
        if(x == 7){
                valid = false;
                return w_0;        
        }
        
        for(y = 0; y < 8; y++){
                if(it_lbpt->fallDelay.transitionIndices[y] >= slew)
                        break;
        }
        
        if(y == 8){
                valid = false;
                return w_0;
        }
        
        //p1---------p3
        //-------------
        //-------------
        //p2---------p4
        pair<int, int> p1, p2, p3, p4;
        if(x >= 3){
                p3.first = min(x + x_half_floor, 6);
                p1.first = max(0, p3.first - x_width);
                p2.first = p1.first;
                p4.first = p3.first;
        }
        else{
                p1.first = max(x - x_half_floor, 0);
                p3.first = min(p1.first + x_width, 6);
                p2.first = p1.first;
                p4.first = p3.first;
        }
        
        if(y >= 4){
                p2.second = min(y + y_half_floor, 7);
                p1.second = max(p2.second - y_width, 0);
                p3.second = p1.second;
                p4.second = p2.second;
        }
        else{
                p2.second = min(y + y_half_floor, 7);
                p1.second = max(p2.second - y_width, 0);
                p3.second = p1.second;
                p4.second = p2.second;
        }
        
        window w;
        pair<int, int> startpt;
        
        startpt.first = p1.first;
        startpt.second = p1.second;
        w.x_width = x_width;
        w.y_width = y_width;
        w.startpt = startpt;
        
        /*
        //always in the middle
        startpt.first = max(3 - x_half_floor, 0);
        startpt.second = max(4 - y_half_ceil, 0);
        w.x_width = min(6 - startpt.first, x_width);
        w.y_width = min(7 - startpt.second, y_width);
        w.startpt = startpt;
        */
        valid = true;
        return w;
}


double errorComputation(vector<LibParserTimingInfo>::iterator it_lbpt, window w, vec coeff, const int mode)
{
        vec cur;
        bool valid;
        double error = 0.0;
        for(int i = w.startpt.first; i < w.startpt.first + w.x_width; i++){
                for(int j = w.startpt.second; j < w.startpt.second + w.y_width; j++){
                        cur.x = it_lbpt->fallDelay.loadIndices[i];
                        cur.y = it_lbpt->fallDelay.transitionIndices[j];
                        double temp = coeff.x * cur.x + coeff.y * cur.y + coeff.z;
                        cur.z = it_lbpt->fallDelay(cur.x, cur.y, valid);
                        error += pow((cur.z - temp), 2); 
                        assert(valid = true);
                }
        }
        
        error = sqrt(error);
        return error;
}


vector<vec> planeSelect(vector<LibParserTimingInfo>::iterator it_lbpt, vector<vec> points, const int mode)
{
        assert(points.size() == 5);
        vec p1 = points[0];
        vec p2 = points[1];
        vec p3 = points[2];
        vec p4 = points[3];
        vec mid_P = points[4];
        
        bool valid = false;
        vec mid_M;
        if(mode == 0){
                mid_M.x = it_lbpt->fallDelay.loadIndices[3];
                mid_M.y = it_lbpt->fallDelay.transitionIndices[4];
                mid_M.z = it_lbpt->fallDelay(mid_M.x, mid_M.y, valid);
                assert(valid = true);
        }else{
                mid_M.x = it_lbpt->fallTransition.loadIndices[3];
                mid_M.y = it_lbpt->fallTransition.transitionIndices[4];
                mid_M.z = it_lbpt->fallTransition(mid_M.x, mid_M.y, valid);
                assert(valid = true);
        }

        //test to see wether the mid_M is within the square bounding box
        if(mid_M.x <= p1.x || mid_M.x >= p3.x || mid_M.y <= p1.y || mid_M.y >= p2.y)
                valid = false;
        else
                valid = true;
        
        //p1---------p3
        //-------------
        //----mid_M----
        //p2---------p4
        int i;
        window window;
        vec v1, v2, nv, final_vec;
        
        if(mode == 0){
                for(i = 0; i < 7; i++)
                        if(it_lbpt->fallDelay.loadIndices[i] > p1.x)
                                break;
        
                assert(i != 0);
                window.startpt.first = --i;
        
                for(i = 0; i < 7; i++)
                        if(it_lbpt->fallDelay.loadIndices[i] > p3.x)
                                break;
                if(i == 7)
                        i = 6;
        
                window.x_width = i - window.startpt.first;
        
                for(i = 0; i < 8; i++)
                        if(it_lbpt->fallDelay.transitionIndices[i] > p1.y)
                                break;
                assert(i != 0);
        
                window.startpt.second = --i;
        
                for(i = 0; i < 8; i++)
                        if(it_lbpt->fallDelay.transitionIndices[i] > p2.y)
                                break;
        
                window.y_width = i - window.startpt.second;
        }
        else{
                for(i = 0; i < 7; i++)
                        if(it_lbpt->fallTransition.loadIndices[i] > p1.x)
                                break;
        
                assert(i != 0);
                window.startpt.first = --i;
        
                for(i = 0; i < 7; i++)
                        if(it_lbpt->fallTransition.loadIndices[i] > p3.x)
                                break;
                if(i == 7)
                        i = 6;
        
                window.x_width = i - window.startpt.first;
        
                for(i = 0; i < 8; i++)
                        if(it_lbpt->fallTransition.transitionIndices[i] > p1.y)
                                break;
                assert(i != 0);
        
                window.startpt.second = --i;
        
                for(i = 0; i < 8; i++)
                        if(it_lbpt->fallTransition.transitionIndices[i] > p2.y)
                                break;
        
                window.y_width = i - window.startpt.second;
        }

        double temp = 0.0;
        multimap <double, vec> myMap; 
        if(valid == true){
        //plane 1
                v1.x = mid_M.x - p3.x;
                v1.y = mid_M.y - p3.y;
                v1.z = mid_M.z - p3.z;
                
                v2.x = mid_M.x - p1.x;
                v2.y = mid_M.y - p1.y;
                v2.z = mid_M.z - p1.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_M.x + (-1.0 * final_vec.y * mid_M.y) + mid_M.z;
                
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 2
                v1.x = p2.x - mid_M.x;
                v1.y = p2.y - mid_M.y;
                v1.z = p2.z - mid_M.z;
                
                v2.x = mid_M.x - p1.x;
                v2.y = mid_M.y - p1.y;
                v2.z = mid_M.z - p1.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_M.x + (-1.0 * final_vec.y * mid_M.y) + mid_M.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 3
                v1.x = p2.x - mid_M.x;
                v1.y = p2.y - mid_M.y;
                v1.z = p2.z - mid_M.z;
                
                v2.x = p4.x - mid_M.x;
                v2.y = p4.y - mid_M.y;
                v2.z = p4.z - mid_M.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_M.x + (-1.0 * final_vec.y * mid_M.y) + mid_M.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 4
                v1.x = p4.x - mid_M.x;
                v1.y = p4.y - mid_M.y;
                v1.z = p4.z - mid_M.z;
                
                v2.x = p3.x - mid_M.x;
                v2.y = p3.y - mid_M.y;
                v2.z = p3.z - mid_M.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_M.x + (-1.0 * final_vec.y * mid_M.y) + mid_M.z;                
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        }
        
        //test to see wether the mid_P is within the square bounding box
        if(mid_P.x <= p1.x || mid_P.x >= p3.x || mid_P.y <= p1.y || mid_P.y >= p2.y)
                valid = false;
        else
                valid = true;
        
        if(valid == true){
        //plane 1
                v1.x = mid_M.x - p3.x;
                v1.y = mid_M.y - p3.y;
                v1.z = mid_M.z - p3.z;
                
                v2.x = mid_P.x - p1.x;
                v2.y = mid_P.y - p1.y;
                v2.z = mid_P.z - p1.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_P.x + (-1.0 * final_vec.y * mid_P.y) + mid_P.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 2
                v1.x = p2.x - mid_P.x;
                v1.y = p2.y - mid_P.y;
                v1.z = p2.z - mid_P.z;
                
                v2.x = mid_P.x - p1.x;
                v2.y = mid_P.y - p1.y;
                v2.z = mid_P.z - p1.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_P.x + (-1.0 * final_vec.y * mid_P.y) + mid_P.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 3
                v1.x = p2.x - mid_P.x;
                v1.y = p2.y - mid_P.y;
                v1.z = p2.z - mid_P.z;
                
                v2.x = p4.x - mid_P.x;
                v2.y = p4.y - mid_P.y;
                v2.z = p4.z - mid_P.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_P.x + (-1.0 * final_vec.y * mid_P.y) + mid_P.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 4
                v1.x = p4.x - mid_P.x;
                v1.y = p4.y - mid_P.y;
                v1.z = p4.z - mid_P.z;
                
                v2.x = p3.x - mid_P.x;
                v2.y = p3.y - mid_P.y;
                v2.z = p3.z - mid_P.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * mid_P.x + (-1.0 * final_vec.y * mid_P.y) + mid_P.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        }
        
        //planes that only consider the four corners
                //plane 1
                v1.x = p2.x - p1.x;
                v1.y = p2.y - p1.y;
                v1.z = p2.z - p1.z;
                
                v2.x = p3.x - p1.x;
                v2.y = p3.y - p1.y;
                v2.z = p3.z - p1.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * p1.x + (-1.0 * final_vec.y * p1.y) + p1.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 2
                v1.x = p2.x - p1.x;
                v1.y = p2.y - p1.y;
                v1.z = p2.z - p1.z;
                
                v2.x = p4.x - p1.x;
                v2.y = p4.y - p1.y;
                v2.z = p4.z - p1.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * p1.x + (-1.0 * final_vec.y * p1.y) + p1.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 3
                v1.x = p4.x - p1.x;
                v1.y = p4.y - p1.y;
                v1.z = p4.z - p1.z;
                
                v2.x = p3.x - p1.x;
                v2.y = p3.y - p1.y;
                v2.z = p3.z - p1.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * p1.x + (-1.0 * final_vec.y * p1.y) + p1.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
        //plane 4
                v1.x = p4.x - p2.x;
                v1.y = p4.y - p2.y;
                v1.z = p4.z - p2.z;
                
                v2.x = p3.x - p2.x;
                v2.y = p3.y - p2.y;
                v2.z = p3.z - p2.z;
                
                calCrossproduct(v1, v2, nv);
                
                if(fabs(nv.z) >= 0.0001){
                        final_vec.x = -1.0 * nv.x / nv.z;
                        final_vec.y = -1.0 * nv.y / nv.z;
                        final_vec.z = -1.0 * final_vec.x * p2.x + (-1.0 * final_vec.y * p2.y) + p2.z;
                        
                        temp = errorComputation(it_lbpt, window, final_vec, mode);
                        myMap.insert(pair<double, vec>(temp, final_vec));
                }
                
                assert(myMap.size() >= 2);
                vector<vec> planeCoeff;
                multimap<double,vec>::iterator it;
                i = 0;
                for(it = myMap.begin(); it != myMap.end(); it++){
                        planeCoeff.push_back((*it).second);
                        i++;
                        if(i == 2)
                                break;
                }
                
                return planeCoeff;
}


#ifdef RADIUS_BASED
vector<vec> lutApproximation(LibParserCellInfo &cell, string &fromPin, int radius, double outputLoading,
                             double inputSlew, const int mode, bool &approxValid)
{
        int i; vec med;
        bool valid, atCorner = false;
        double max_x, max_y; //for region comparison
        int x_width, y_width, med_x, med_y;
        vector<vec> myVectors;
        pair<int, int> startpt;
        
        vector<LibParserTimingInfo>::iterator it_lbpt;
        //cout << cell.name << "\t" << cell.footprint << endl;
        for(it_lbpt = cell.timingArcs.begin(); it_lbpt != cell.timingArcs.end(); it_lbpt++)
                if(it_lbpt->fromPin == fromPin)
                        break;
       
        assert(mode == 0 || mode == 1);
        assert(it_lbpt != cell.timingArcs.end());
       
        vec p1, p2, p3, p4, v1, v2, nv, final_vec;
        //for DFF
        if(cell.footprint == "ms00"){
               
                v1.x = 5.0/128.0;
                v1.y = 0.0;
                v1.z = 16.0;
               
                v2.x = 5.0/128.0;
                v2.y = 0.0;
                v2.z = 16.0;
                myVectors.push_back(v1);
                myVectors.push_back(v2);
                
                approxValid = true;
                return myVectors;
        }
       
        if(opt_rd != 0){
                
                med.x = outputLoading;
                med.y = inputSlew;
                
                if(mode == 0)
                {
                        max_x = it_lbpt->fallDelay.loadIndices[6];
                        max_y = it_lbpt->fallDelay.transitionIndices[7];
                        med.z = it_lbpt->fallDelay(med.x, med.y, valid);
                        
                        if(valid == false){
                                //cout << "$1" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                       
                        //cout << "med.x " << med.x << "\t" << "med.y " << med.y << "\t" << "med.z " << med.z << endl;
                        for(i = 0; i < 7; i++)
                                if(it_lbpt->fallDelay.loadIndices[i] > med.x)
                                        break;
               
                        if(i == 0 || i == 7){
                                //cout << "$2" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        med_x = i - 1;
               
                        for(i = 0; i < 8; i++)
                                if(it_lbpt->fallDelay.transitionIndices[i] > med.y)
                                        break;
                       
                        if(i == 0 || i == 8){
                                //cout << "$3" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        med_y = i - 1;
                }
                else{
                        max_x = it_lbpt->fallTransition.loadIndices[6];
                        max_y = it_lbpt->fallTransition.transitionIndices[7];
                        med.z = it_lbpt->fallTransition(med.x, med.y, valid);
                       
                        if(valid == false){
                                //cout << "$4" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                       
                        //cout << "med.x " << med.x << "\t" << "med.y " << med.y << "\t" << "med.z " << med.z << endl;
                        for(i = 0; i < 7; i++)
                                if(it_lbpt->fallTransition.loadIndices[i] > med.x)
                                        break;
               
                        if(i == 0 || i == 7){
                                //cout << "$5" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        med_x = i - 1;
               
                        for(i = 0; i < 8; i++)
                                if(it_lbpt->fallTransition.transitionIndices[i] > med.y)
                                        break;
                       
                        if(i == 0 || i == 8){
                                //cout << "$6" << endl;
                                //cout << med.y << "\t" << max_y << "\t" << it_lbpt->fallTransition.transitionIndices[0] << endl;
                                //approxValid = false;
                                return myVectors;
                        }
                        med_y = i - 1;
                }
               
                assert(radius <= 3);
       
                if(max_x - med.x <= 5.0 || max_y - med.y <= 10.0)//my assumption
                        atCorner = true;
        }
       
        if(opt_rd == 0){
                startpt.first = 0;
                startpt.second = 0;
                x_width = 6;
                y_width = 7;
                med_x = 3;
                med_y = 3;
                med.x = it_lbpt->fallDelay.loadIndices[med_x];
                med.y = it_lbpt->fallDelay.transitionIndices[med_y];
                med.z = it_lbpt->fallDelay(med.x, med.y, valid);        
        }
        else{
                startpt.first = max(0, med_x - radius);
                startpt.second = max(0, med_y - radius);
                x_width = min(6, startpt.first + 2 * radius) - startpt.first;
                y_width = min(7, startpt.second + 2 * radius) - startpt.second;
        }
       
        assert(startpt.first + x_width <= 6);
        assert(startpt.second + y_width <= 7);
        
        // 2 for delays, 2 for transitions
        if(mode == 0){
                //_______________________________Delay______________________________________
                //point 1
                p1.x = it_lbpt->fallDelay.loadIndices[startpt.first];
                p1.y = it_lbpt->fallDelay.transitionIndices[startpt.second];
                p1.z = it_lbpt->fallDelay(p1.x, p1.y, valid);
                assert(valid == true);
       
                //point 2
                p2.x = it_lbpt->fallDelay.loadIndices[startpt.first];
                p2.y = it_lbpt->fallDelay.transitionIndices[startpt.second + y_width];
                p2.z = it_lbpt->fallDelay(p2.x, p2.y, valid);
                assert(valid == true);
       
                //point 3
                p3.x = it_lbpt->fallDelay.loadIndices[startpt.first + x_width];
                p3.y = it_lbpt->fallDelay.transitionIndices[startpt.second];
                p3.z = it_lbpt->fallDelay(p3.x, p3.y, valid);
                assert(valid == true);
               
                //point 4
                p4.x = it_lbpt->fallDelay.loadIndices[startpt.first + x_width];
                p4.y = it_lbpt->fallDelay.transitionIndices[startpt.second + y_width];
                p4.z = it_lbpt->fallDelay(p4.x, p4.y, valid);
                assert(valid == true);

#ifndef RANDOM_PLANE               
                vector<vec> points;
                points.push_back(p1);
                points.push_back(p2);
                points.push_back(p3);
                points.push_back(p4);
                points.push_back(med);
                myVectors = planeSelect(it_lbpt, points, mode);
                //assert(myVectors.size() == 2);
#else
                //Plane 1
                //vector 1
                v1.x = med.x - p2.x;
                v1.y = med.y - p2.y;
                v1.z = med.z - p2.z;
                
                //vector 2
                v2.x = med.x - p1.x;
                v2.y = med.y - p1.y;
                v2.z = med.z - p1.z;
       
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //if(nv.z <= 0.00001)
                //cout << v1.x << "\t" << v1.y << "\t" << v1.z << endl;
               
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
       
                myVectors.push_back(final_vec);
               
                //Plane 2
                if(atCorner == false){
                        //vector 1
                        v1.x = p3.x - med.x;
                        v1.y = p3.y - med.y;
                        v1.z = p3.z - med.z;
                       
                        //vector 2
                        v2.x = p4.x - med.x;
                        v2.y = p4.y - med.y;
                        v2.z = p4.z - med.z;
                }
                else{
                        //vector 1
                        v1.x = p3.x - p1.x;
                        v1.y = p3.y - p1.y;
                        v1.z = p3.z - p1.z;
                       
                        //vector 2
                        v2.x = med.x - p1.x;
                        v2.y = med.y - p1.y;
                        v2.z = med.z - p1.z;
                }
               
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
               
                myVectors.push_back(final_vec);
#endif
                
#ifdef MORE_PLANE                
                //Plane 3
                if(atCorner == false){
                        //vector 1
                        v1.x = med.x - p2.x;
                        v1.y = med.y - p2.y;
                        v1.z = med.z - p2.z;
                       
                        //vector 2
                        v2.x = med.x - p3.x;
                        v2.y = med.y - p3.y;
                        v2.z = med.z - p3.z;        
                }
                else{
                        //vector 1
                        v1.x = p4.x - p2.x;
                        v1.y = p4.y - p2.y;
                        v1.z = p4.z - p2.z;
                       
                        //vector 2
                        v2.x = p4.x - p1.x;
                        v2.y = p4.y - p1.y;
                        v2.z = p4.z - p1.z;
                }
               
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * p2.x + (-1.0 * final_vec.y * p2.y) + p2.z;
               
                myVectors.push_back(final_vec);
#endif
        }
        else if(mode == 1){
                //_______________________________Transition______________________________________        
                //point 1
                //cout << "cordinate for start is " << startpt.first << "\t" << startpt.second << endl;
                p1.x = it_lbpt->fallTransition.loadIndices[startpt.first];
                //cout << "p1.x is " << p1.x << endl;
                p1.y = it_lbpt->fallTransition.transitionIndices[startpt.second];
                //cout << "p1.y is " << p1.y << endl;
                p1.z = it_lbpt->fallTransition(p1.x, p1.y, valid);
                assert(valid == true);
       
                //point 2
                p2.x = it_lbpt->fallTransition.loadIndices[startpt.first];
                p2.y = it_lbpt->fallTransition.transitionIndices[startpt.second + y_width];
                p2.z = it_lbpt->fallTransition(p2.x, p2.y, valid);
                assert(valid == true);
       
                //point 3
                p3.x = it_lbpt->fallTransition.loadIndices[startpt.first + x_width];
                p3.y = it_lbpt->fallTransition.transitionIndices[startpt.second];
                p3.z = it_lbpt->fallTransition(p3.x, p3.y, valid);
                assert(valid == true);
       
                //point 4
                p4.x = it_lbpt->fallTransition.loadIndices[startpt.first + x_width];
                p4.y = it_lbpt->fallTransition.transitionIndices[startpt.second + y_width];
                p4.z = it_lbpt->fallTransition(p4.x, p4.y, valid);
                assert(valid == true);

#ifndef RANDOM_PLANE               
                vector<vec> points;
                points.push_back(p1);
                points.push_back(p2);
                points.push_back(p3);
                points.push_back(p4);
                points.push_back(med);
                myVectors = planeSelect(it_lbpt, points, mode);
                //assert(myVectors.size() == 2);
#else               
                //Plane 1
                //vector 1
                v1.x = med.x - p2.x;
                v1.y = med.y - p2.y;
                v1.z = med.z - p2.z;

                //vector 2
                v2.x = med.x - p1.x;
                v2.y = med.y - p1.y;
                v2.z = med.z - p1.z;
               
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
       
                myVectors.push_back(final_vec);
       
                //Plane 2
                if(atCorner == false){
                        //vector 1
                        v1.x = p3.x - med.x;
                        v1.y = p3.y - med.y;
                        v1.z = p3.z - med.z;
                       
                        //vector 2
                        v2.x = p4.x - med.x;
                        v2.y = p4.y - med.y;
                        v2.z = p4.z - med.z;
                }
                else{
                        //vector 1
                        v1.x = p3.x - p1.x;
                        v1.y = p3.y - p1.y;
                        v1.z = p3.z - p1.z;
                       
                        //vector 2
                        v2.x = med.x - p1.x;
                        v2.y = med.y - p1.y;
                        v2.z = med.z - p1.z;
                }
               
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
               
                myVectors.push_back(final_vec);
#endif
#ifdef MORE_PLANE                
                //Plane 3
                if(atCorner == false){
                        //vector 1
                        v1.x = med.x - p2.x;
                        v1.y = med.y - p2.y;
                        v1.z = med.z - p2.z;
                       
                        //vector 2
                        v2.x = med.x - p3.x;
                        v2.y = med.y - p3.y;
                        v2.z = med.z - p3.z;        
                }
                else{
                        //vector 1
                        v1.x = p4.x - p2.x;
                        v1.y = p4.y - p2.y;
                        v1.z = p4.z - p2.z;
                       
                        //vector 2
                        v2.x = p4.x - p1.x;
                        v2.y = p4.y - p1.y;
                        v2.z = p4.z - p1.z;
                }
               
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * p2.x + (-1.0 * final_vec.y * p2.y) + p2.z;
               
                myVectors.push_back(final_vec);
#endif
               
        }
       
        approxValid = true;
        return myVectors;
}

#else
vector<vec> lutApproximation(LibParserCellInfo &cell, string &fromPin, window w, double outputLoading, 
                             double inputSlew, const int mode, bool &approxValid)
{
        int i; vec med;
        bool valid, atCorner = false;
        double max_x, max_y; //for region comparison
        int x_width, y_width, med_x, med_y;
        vector<vec> myVectors;
        pair<int, int> startpt;
        
        vector<LibParserTimingInfo>::iterator it_lbpt;
        //cout << cell.name << "\t" << cell.footprint << endl;
        for(it_lbpt = cell.timingArcs.begin(); it_lbpt != cell.timingArcs.end(); it_lbpt++)
                if(it_lbpt->fromPin == fromPin)
                        break;
        
        assert(mode == 0 || mode == 1);
        assert(it_lbpt != cell.timingArcs.end());
        
        vec p1, p2, p3, p4, v1, v2, nv, final_vec;
        //for DFF
        if(cell.footprint == "ms00"){
                
                v1.x = 5.0/128.0;
                v1.y = 0.0;
                v1.z = 16.0;
                
                v2.x = 5.0/128.0;
                v2.y = 0.0;
                v2.z = 16.0;
                myVectors.push_back(v1);
                myVectors.push_back(v2);
                
                approxValid = true;
                return myVectors;
        }
        
        if(opt_rd != 0){
                
                med.x = outputLoading;
                med.y = inputSlew;
                
                if(mode == 0)
                {
                        max_x = it_lbpt->fallDelay.loadIndices[6];
                        max_y = it_lbpt->fallDelay.transitionIndices[7];
                        med.z = it_lbpt->fallDelay(med.x, med.y, valid);
                        
                        if(valid == false){
                                //cout << "$1" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        
                        //cout << "med.x " << med.x << "\t" << "med.y " << med.y << "\t" << "med.z " << med.z << endl;
                        
                        for(i = 0; i < 7; i++)
                                if(it_lbpt->fallDelay.loadIndices[i] > med.x)
                                        break;
                
                        if(i == 0 || i == 7){
                                //cout << "$2" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        med_x = i - 1;
                
                        for(i = 0; i < 8; i++)
                                if(it_lbpt->fallDelay.transitionIndices[i] > med.y)
                                        break;
                        
                        if(i == 0 || i == 8){
                                //cout << "$3" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        med_y = i - 1;
                }
                else{
                        max_x = it_lbpt->fallTransition.loadIndices[6];
                        max_y = it_lbpt->fallTransition.transitionIndices[7];
                        med.z = it_lbpt->fallTransition(med.x, med.y, valid);
                        
                        if(valid == false){
                                //cout << "$4" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        
                        //cout << "med.x " << med.x << "\t" << "med.y " << med.y << "\t" << "med.z " << med.z << endl;
                        
                        for(i = 0; i < 7; i++)
                                if(it_lbpt->fallTransition.loadIndices[i] > med.x)
                                        break;
                
                        if(i == 0 || i == 7){
                                //cout << "$5" << endl;
                                approxValid = false;
                                return myVectors;
                        }
                        med_x = i - 1;
                
                        for(i = 0; i < 8; i++)
                                if(it_lbpt->fallTransition.transitionIndices[i] > med.y)
                                        break;
                        
                        if(i == 0 || i == 8){
                                //cout << "$6" << endl;
                                //cout << med.y << "\t" << max_y << "\t" << it_lbpt->fallTransition.transitionIndices[0] << endl;
                                //approxValid = false;
                                return myVectors;
                        }
                        med_y = i - 1;
                }
                
                if(max_x - med.x <= 5.0 || max_y - med.y <= 10.0)//my assumption
                        atCorner = true;
        }
        
        if(opt_rd == 0){
                startpt.first = 0;
                startpt.second = 0;
                x_width = 6;
                y_width = 7;
                med_x = 3;
                med_y = 3;
                med.x = it_lbpt->fallDelay.loadIndices[med_x];
                med.y = it_lbpt->fallDelay.transitionIndices[med_y];
                med.z = it_lbpt->fallDelay(med.x, med.y, valid);        
        }
        else{
                startpt.first = w.startpt.first;
                startpt.second = w.startpt.second;
                x_width = w.x_width;
                y_width = w.y_width;
        }
        
        assert(startpt.first + x_width <= 6);
        assert(startpt.second + y_width <= 7);

        // 2 for delays, 2 for transitions
        if(mode == 0){
                //_______________________________Delay______________________________________
                //point 1
                p1.x = it_lbpt->fallDelay.loadIndices[startpt.first];
                p1.y = it_lbpt->fallDelay.transitionIndices[startpt.second];
                p1.z = it_lbpt->fallDelay(p1.x, p1.y, valid);
                assert(valid == true);
        
                //point 2
                p2.x = it_lbpt->fallDelay.loadIndices[startpt.first];
                p2.y = it_lbpt->fallDelay.transitionIndices[startpt.second + y_width];
                p2.z = it_lbpt->fallDelay(p2.x, p2.y, valid);
                assert(valid == true);
        
                //point 3
                p3.x = it_lbpt->fallDelay.loadIndices[startpt.first + x_width];
                p3.y = it_lbpt->fallDelay.transitionIndices[startpt.second];
                p3.z = it_lbpt->fallDelay(p3.x, p3.y, valid);
                assert(valid == true);
                
                //point 4
                p4.x = it_lbpt->fallDelay.loadIndices[startpt.first + x_width];
                p4.y = it_lbpt->fallDelay.transitionIndices[startpt.second + y_width];
                p4.z = it_lbpt->fallDelay(p4.x, p4.y, valid);
                assert(valid == true);

#ifndef RANDOM_PLANE               
                vector<vec> points;
                points.push_back(p1);
                points.push_back(p2);
                points.push_back(p3);
                points.push_back(p4);
                points.push_back(med);
                myVectors = planeSelect(it_lbpt, points, mode);
                //assert(myVectors.size() == 2);
#else                
                //Plane 1
                //vector 1
                v1.x = med.x - p2.x;
                v1.y = med.y - p2.y;
                v1.z = med.z - p2.z;

                //vector 2
                v2.x = med.x - p1.x;
                v2.y = med.y - p1.y;
                v2.z = med.z - p1.z;
        
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //if(nv.z <= 0.00001)
                //cout << v1.x << "\t" << v1.y << "\t" << v1.z << endl;
                
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
        
                myVectors.push_back(final_vec);
                
                //Plane 2
                if(atCorner == false){
                        //vector 1
                        v1.x = p3.x - med.x;
                        v1.y = p3.y - med.y;
                        v1.z = p3.z - med.z;
                        
                        //vector 2
                        v2.x = p4.x - med.x;
                        v2.y = p4.y - med.y;
                        v2.z = p4.z - med.z;
                }
                else{
                        //vector 1
                        v1.x = p3.x - p1.x;
                        v1.y = p3.y - p1.y;
                        v1.z = p3.z - p1.z;
                        
                        //vector 2
                        v2.x = med.x - p1.x;
                        v2.y = med.y - p1.y;
                        v2.z = med.z - p1.z;
                }
                
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
                
                myVectors.push_back(final_vec);
#endif
#ifdef MORE_PLANE                
                //Plane 3
                if(atCorner == false){
                        //vector 1
                        v1.x = med.x - p2.x;
                        v1.y = med.y - p2.y;
                        v1.z = med.z - p2.z;
                        
                        //vector 2
                        v2.x = med.x - p3.x;
                        v2.y = med.y - p3.y;
                        v2.z = med.z - p3.z;        
                }
                else{
                        //vector 1
                        v1.x = p4.x - p2.x;
                        v1.y = p4.y - p2.y;
                        v1.z = p4.z - p2.z;
                        
                        //vector 2
                        v2.x = p4.x - p1.x;
                        v2.y = p4.y - p1.y;
                        v2.z = p4.z - p1.z;
                }
                
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * p2.x + (-1.0 * final_vec.y * p2.y) + p2.z;
                
                myVectors.push_back(final_vec);
#endif
        }
        else if(mode == 1){
                //_______________________________Transition______________________________________        
                //point 1
                //cout << "cordinate for start is " << startpt.first << "\t" << startpt.second << endl;
                p1.x = it_lbpt->fallTransition.loadIndices[startpt.first];
                //cout << "p1.x is " << p1.x << endl;
                p1.y = it_lbpt->fallTransition.transitionIndices[startpt.second];
                //cout << "p1.y is " << p1.y << endl;
                p1.z = it_lbpt->fallTransition(p1.x, p1.y, valid);
                assert(valid == true);
        
                //point 2
                p2.x = it_lbpt->fallTransition.loadIndices[startpt.first];
                p2.y = it_lbpt->fallTransition.transitionIndices[startpt.second + y_width];
                p2.z = it_lbpt->fallTransition(p2.x, p2.y, valid);
                assert(valid == true);
        
                //point 3
                p3.x = it_lbpt->fallTransition.loadIndices[startpt.first + x_width];
                p3.y = it_lbpt->fallTransition.transitionIndices[startpt.second];
                p3.z = it_lbpt->fallTransition(p3.x, p3.y, valid);
                assert(valid == true);
        
                //point 4
                p4.x = it_lbpt->fallTransition.loadIndices[startpt.first + x_width];
                p4.y = it_lbpt->fallTransition.transitionIndices[startpt.second + y_width];
                p4.z = it_lbpt->fallTransition(p4.x, p4.y, valid);
                assert(valid == true);

#ifndef RANDOM_PLANE               
                vector<vec> points;
                points.push_back(p1);
                points.push_back(p2);
                points.push_back(p3);
                points.push_back(p4);
                points.push_back(med);
                myVectors = planeSelect(it_lbpt, points, mode);
                //assert(myVectors.size() == 2);
#else                
                //Plane 1
                //vector 1
                v1.x = med.x - p2.x;
                v1.y = med.y - p2.y;
                v1.z = med.z - p2.z;

                //vector 2
                v2.x = med.x - p1.x;
                v2.y = med.y - p1.y;
                v2.z = med.z - p1.z;
                
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
        
                myVectors.push_back(final_vec);
        
                //Plane 2
                if(atCorner == false){
                        //vector 1
                        v1.x = p3.x - med.x;
                        v1.y = p3.y - med.y;
                        v1.z = p3.z - med.z;
                        
                        //vector 2
                        v2.x = p4.x - med.x;
                        v2.y = p4.y - med.y;
                        v2.z = p4.z - med.z;
                }
                else{
                        //vector 1
                        v1.x = p3.x - p1.x;
                        v1.y = p3.y - p1.y;
                        v1.z = p3.z - p1.z;
                        
                        //vector 2
                        v2.x = med.x - p1.x;
                        v2.y = med.y - p1.y;
                        v2.z = med.z - p1.z;
                }
                
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * med.x + (-1.0 * final_vec.y * med.y) + med.z;
                
                myVectors.push_back(final_vec);
#endif
#ifdef MORE_PLANE                
                //Plane 3
                if(atCorner == false){
                        //vector 1
                        v1.x = med.x - p2.x;
                        v1.y = med.y - p2.y;
                        v1.z = med.z - p2.z;
                        
                        //vector 2
                        v2.x = med.x - p3.x;
                        v2.y = med.y - p3.y;
                        v2.z = med.z - p3.z;        
                }
                else{
                        //vector 1
                        v1.x = p4.x - p2.x;
                        v1.y = p4.y - p2.y;
                        v1.z = p4.z - p2.z;
                        
                        //vector 2
                        v2.x = p4.x - p1.x;
                        v2.y = p4.y - p1.y;
                        v2.z = p4.z - p1.z;
                }
                
                //cross product v1 X v2
                calCrossproduct(v1, v2, nv);
                //Plane formula a(x-x0)+b(y-y0)+c(z-z0)+d=0
                //Let x0,y0,z0 be one of the three points, then d = 0.
                final_vec.x = -1.0 * nv.x / nv.z;
                final_vec.y = -1.0 * nv.y / nv.z;
                final_vec.z = -1.0 * final_vec.x * p2.x + (-1.0 * final_vec.y * p2.y) + p2.z;
                
                myVectors.push_back(final_vec);
#endif
                
        }
        
        approxValid = true;
        return myVectors;
}
#endif


bool lessLeakage(const string &s1, const string &s2)
{
        LibParserCellInfo *LPCI1, *LPCI2;
        LPCI1 = &(lpPtr->mapStandardLibraryCells[s1]);
        LPCI2 = &(lpPtr->mapStandardLibraryCells[s2]);
        
        if(LPCI1->leakagePower == LPCI2->leakagePower)
                return (LPCI1->area < LPCI2->area);
        else
                return (LPCI1->leakagePower < LPCI2->leakagePower);
}


void minLeakage (CircuitGraph& graphIn)
{
        int i,j;
        int numberOfCells;
        LibParserCellInfo *LPCI;
        
        double temp_leakage;
        string selected_cell_name;
        cout << "Choosing minimum leakage cells...." << endl;
        
        int vn = boost::num_vertices(graphIn) ;
        cout << "Number of gates: " << vn << endl ;
        
        for( i = 0; i < vn; i++)
        {               
                if(graphIn[i].gateType == 2)
                {
                        //sort the cells of same footprint in less leakage order
                        std::sort(graphIn[i].listOfCells.begin(),graphIn[i].listOfCells.end(),lessLeakage);
                        graphIn[ i ].cellName = graphIn[i].listOfCells[0];
                        graphIn[ i ].previousIndex = 0; // this is important
                }
        }
}


bool moreViolation(const int i1, const int i2)
{
        return ((*graphInPtr)[i1].Slew_Violation + (*graphInPtr)[i1].Slack_Violation + (*graphInPtr)[i1].Cap_Violation > 
                (*graphInPtr)[i2].Slew_Violation + (*graphInPtr)[i2].Slack_Violation + (*graphInPtr)[i2].Cap_Violation);
}


double violationCheck(CircuitGraph& graphIn, bool write_to_file, double* outputLdArray)
{
        //ofstream out("HEHE");
        int count_cap_vio = 0;
        int count_slew_vio = 0;
        int count_slack_vio = 0;
        LibParserCellInfo *LPCI;
        LibParserCellInfo *LPCI_;
        double T_Output_Cap;
        double temp = 0.0;
        double Cap_Violation = 0.0;
        double Slack_Violation = 0.0;
        double Slew_Violation = 0.0;
        lpPtr->default_max_transition = 300.0;// same for all
        
        vector<LibParserPinInfo>::iterator it;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
	boost::graph_traits<CircuitGraph>::out_edge_iterator oi, oi_end;

        long com_gate_cnt = 0;
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
	{
                
                vertex_descriptor in = (*vi) ;
                graphIn[ in ].Slew_Violation = 0.0;
                graphIn[ in ].Slack_Violation = 0.0;
                graphIn[ in ].Cap_Violation = 0.0;

                graphIn[ in ].Slew_Redundancy = 0.0;
                graphIn[ in ].Slack_Redundancy = 0.0;
                graphIn[ in ].Cap_Redundancy = 0.0;
                
                LPCI = &(lpPtr->mapStandardLibraryCells[ graphIn[ in ].cellName ]);
                
                if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                        continue;

                //check the slew violation, for input pins of
                //Combinational and Sequential cells
                if(graphIn[ in ].gateType != 0 && graphIn[ in ].gateType != 1)
                {
                        for(it = LPCI->pins.begin(); it != LPCI->pins.end(); it++)
                                if(it->name != "o")//not the output pins
                                {
                                        double temp_violation = 0.0;
                                        double temp_slack_violation = 0.0;
                                        double temp_redundancy = 0.0;
                                        double temp_slack_redundancy = 0.0;
                                        string tmp_name = graphIn[ in ].instanceName + "/" + it->name;
                                
                                        if(tpPtr->PinsTiming[tmp_name].riseTransition > lpPtr->default_max_transition)
                                                temp_violation += tpPtr->PinsTiming[tmp_name].riseTransition - lpPtr->default_max_transition;
                                
                                        if(tpPtr->PinsTiming[tmp_name].fallTransition > lpPtr->default_max_transition)
                                                temp_violation += tpPtr->PinsTiming[tmp_name].fallTransition - lpPtr->default_max_transition;
                                
                                        if(tpPtr->PinsTiming[tmp_name].riseSlack < 0)
                                                temp_slack_violation += abs(tpPtr->PinsTiming[tmp_name].riseSlack);
                                        else
                                                temp_slack_redundancy += abs(tpPtr->PinsTiming[tmp_name].riseSlack);
                                
                                        if(tpPtr->PinsTiming[tmp_name].fallSlack < 0)
                                                temp_slack_violation += abs(tpPtr->PinsTiming[tmp_name].fallSlack);
                                        else
                                                temp_slack_redundancy += abs(tpPtr->PinsTiming[tmp_name].fallSlack);
                                
                                        graphIn[ in ].Slew_Violation += temp_violation;                                
                                        graphIn[ in ].Slack_Violation += temp_slack_violation;                                
                                        graphIn[ in ].Slack_Redundancy += temp_slack_redundancy;
                                        
                                        Slew_Violation += temp_violation;
                                }
                
                        //check the slack violation, for sequential gates
                        if(graphIn[ in ].gateType == 3)
                        {
                                for(it = LPCI->pins.begin(); it != LPCI->pins.end(); it++){
                                        if(it->name != "o" && it->name != "ck")//not the output pins
                                        {
                                                double temp_violation = 0.0;
                                                double temp_slack_redundancy = 0.0;
                                                string tmp_name = graphIn[ in ].instanceName + "/" + it->name;
                                        
                                                if(tpPtr->PinsTiming[tmp_name].riseSlack < 0)
                                                        temp_violation += abs(tpPtr->PinsTiming[tmp_name].riseSlack);
                                                else
                                                        temp_slack_redundancy += abs(tpPtr->PinsTiming[tmp_name].riseSlack);
                                                
                                                if(tpPtr->PinsTiming[tmp_name].fallSlack < 0)
                                                        temp_violation += abs(tpPtr->PinsTiming[tmp_name].fallSlack);
                                                else
                                                        temp_slack_redundancy += abs(tpPtr->PinsTiming[tmp_name].fallSlack);

                                                //for sequential circuit, should be
                                                //only one net connect to DFF input
                                                
                                                graphIn[ in ].Slack_Violation += temp_violation;
                                                graphIn[ in ].Slack_Redundancy += temp_slack_redundancy;
                                                Slack_Violation += temp_violation;
                                        }
                                }
                        }
                }
                
                //check the output cap violation
                temp = 0.0;
                T_Output_Cap = 0.0;
                int flag = 0;
                for ( boost::tie(oi, oi_end) = boost::out_edges(in, graphIn) ; oi != oi_end ; ++oi )
                {
                        //finding the pin connect to PI, for driver,
                        //instance name and pin name and cell name are same
                        double temp_slack_violation = 0.0;
                        double temp_slew_violation = 0.0;
                        double temp_slack_redundancy = 0.0;
                
                        //check the slack and slew violation, for POs
                        if(graphIn[target((*oi), graphIn)].gateType == 1)//is PO
                        {
                                
                                if(tpPtr->PortsTiming[graphIn[(*oi)].netName].riseSlack < 0)
                                        temp_slack_violation += abs(tpPtr->PortsTiming[graphIn[(*oi)].netName].riseSlack);
                                else
                                        temp_slack_redundancy += abs(tpPtr->PortsTiming[graphIn[(*oi)].netName].riseSlack);
                                       
                                if(tpPtr->PortsTiming[graphIn[(*oi)].netName].fallSlack < 0)
                                        temp_slack_violation += abs(tpPtr->PortsTiming[graphIn[(*oi)].netName].fallSlack);
                                else
                                        temp_slack_redundancy += abs(tpPtr->PortsTiming[graphIn[(*oi)].netName].fallSlack);
                                
                                if(tpPtr->PinsTiming[graphIn[(*oi)].netName].riseTransition > lpPtr->default_max_transition)
                                        temp_slew_violation += tpPtr->PinsTiming[graphIn[(*oi)].netName].riseTransition - lpPtr->default_max_transition;
                                
                                if(tpPtr->PinsTiming[graphIn[(*oi)].netName].fallTransition > lpPtr->default_max_transition)
                                        temp_slew_violation += tpPtr->PinsTiming[graphIn[(*oi)].netName].fallTransition - lpPtr->default_max_transition;
                                
                                Slack_Violation += temp_slack_violation;
                                Slew_Violation += temp_slew_violation;
                                
                                //for PO, the violation comes from its
                                //fan-in gates
                                graphIn[source((*oi), graphIn)].Slack_Violation += temp_slack_violation;                                
                                graphIn[source((*oi), graphIn)].Slew_Violation += temp_slew_violation;
                                graphIn[source((*oi), graphIn)].Slack_Redundancy += temp_slack_redundancy;
                        }
                        
                        //net name should be unique
                        temp += graphIn[(*oi)].netParasiticLoad;
                        LPCI_ = &(lpPtr->mapStandardLibraryCells[graphIn[target((*oi), graphIn)].cellName]); 
                        
                        for( it = LPCI_->pins.begin(); it != LPCI_->pins.end(); it++ )
                                if(it->name == graphIn[(*oi)].targetPinName)
                                        break;
                        
                        if(it != LPCI_->pins.end()){
                                if(flag == 0)
                                        T_Output_Cap += (*it).capacitance;
                                flag = 1;
                        }
                        else//should go back to the spec file for the output load
                        {
                                if(flag == 0)
                                        T_Output_Cap += 4; //we assume all case is
                                //4 for this benchmark set
                                flag = 1;
                                //T_Output_Cap += sdcPtr->outputs_load[graphIn[(*oi)].netName];
                        }
                }
                
                //T_Output_Cap += temp / boost::out_degree(in, graphIn);
                for( it = LPCI->pins.begin(); it != LPCI->pins.end(); it++ )
                        if(it->name == "o")
                                break;
                
                if(it == LPCI->pins.end())
                        cout<<"No Way"<<endl;
                
                if(graphIn[ in ].gateType == 2)
                        outputLdArray[com_gate_cnt] = T_Output_Cap;
                if(graphIn[ in ].gateType == 2 || graphIn[ in ].gateType == 3)
                        com_gate_cnt++;

                if( (*it).maxCapacitance < T_Output_Cap){
                        Cap_Violation += T_Output_Cap - (*it).maxCapacitance;
                        graphIn[ in ].Cap_Violation += T_Output_Cap - (*it).maxCapacitance;
                }
        }
        
        int stat[8];
        memset(stat, 0, 8 * sizeof(int));
        int flag1, flag2, flag3;
        
        int vn = boost::num_vertices(graphIn) ;
        for(int i = 0; i < vn; i++)
        {
                flag1 = 0;
                flag2 = 0;
                flag3 = 0;
                
                if(graphIn[ i ].Cap_Violation > 0.0)
                {
                        flag1 = 1;
                        count_cap_vio++;
                }
                if(graphIn[ i ].Slack_Violation > 0.0)
                {
                        flag2 = 1;
                        count_slack_vio++;
                }
                if(graphIn[ i ].Slew_Violation > 0.0)
                {
                        flag3 = 1;
                        count_slew_vio++;
                }
                
                stat[flag1 + flag2 * 2 + flag3 * 4]++;
        }
        
        //for testing
        /*
          for(int i = 0; i < vn; i++)
          {
          cout<<"Cell Name: "<< graphIn[ i ].cellName <<endl;
          cout<< "Slack Violation is  "<< graphIn[ i ].Slack_Violation << "\t"
          << "Slew Violation is  "<< graphIn[ i ].Slew_Violation << "\t"
          << "Cap Violation is  "<< graphIn[ i ].Cap_Violation << endl;
          }
        */
        
        double Total_Violation = Slew_Violation + Slack_Violation + Cap_Violation;
        if(write_to_file)
        {
                /*out << Slack_Violation << "\t" << Slew_Violation 
                    << "\t" << Cap_Violation << "\t" << Total_Violation 
                    << "\t" << graphIn.accumTotalLeakage(lpPtr->mapStandardLibraryCells) 
                    << "\t" << count_slack_vio << "\t" << count_slew_vio << "\t" << count_cap_vio;*/
        }
        else
        {
                cout<<"From my Evaluation script....."<<endl;
                cout<< "Slack Violation is " << Slack_Violation << endl;
                cout<< "Slew Violation is " << Slew_Violation <<endl;
                cout<< "Cap Violation is " << Cap_Violation << endl;
                cout<< "Total Violation is " << Total_Violation << endl;
                cout << "The leakage power is : " << graphIn.accumTotalLeakage(lpPtr->mapStandardLibraryCells) << endl ;
        }
        
        int mode;
        if(Slack_Violation > Slew_Violation && Slack_Violation > Cap_Violation)
                graphIn.mode = 3;
        else if(Slew_Violation > Slack_Violation && Slew_Violation > Cap_Violation)
                graphIn.mode = 2;
        else if(Cap_Violation > Slack_Violation && Cap_Violation > Slew_Violation)
                graphIn.mode = 1;
        else
                graphIn.mode = 0;
        
        //out.close();
        
        return Total_Violation;
}

void checkApproxFuntions(CircuitGraph& graphIn)
{
        vector<string> footPrints ;
        vector<LibParserPinInfo>::iterator it_pin;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
        
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                
                vertex_descriptor in = (*vi);
                if(graphIn[in].gateType == 0 || graphIn[in].gateType == 1 || graphIn[in].gateType == 3)
                        continue;
                
                if(std::find(footPrints.begin(), footPrints.end(), graphIn[in].footPrint) == footPrints.end()){
                        footPrints.push_back(graphIn[in].footPrint);
                        cout << graphIn[in].footPrint << ":" << endl;
                        for(it_pin = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].pins.begin();
                            it_pin != lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].pins.end(); it_pin++)
                        {
                                if(it_pin->name == "o")
                                        continue;
                                
                                string inputPin = it_pin->name;
                                cout << inputPin << endl;
                                for(int j = 0; j < graphIn[ in ].listOfCells.size(); j++){
                                        vector<vec> approxTerms ;
                                        bool approxValid = false; 
                                        //approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                        //0.0, 0.0, 0, approxValid);//gave fake numbers
                                        
                                        cout << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                        cout << approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                        
                                        approxTerms.clear();
                                        approxValid = false; 
                                        //approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                        //0.0, 0.0, 1, approxValid);//gave fake numbers
                                        
                                        cout << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                        cout << approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                }
                        }
                }
        }        
}

void fixVariable(CircuitGraph& graphIn, NumVarMatrix &Xoi, IloModel &model, const vertex_descriptor in, const unsigned int option)
{
        //fix DFF options
        unsigned int j;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
        assert(graphIn[ in ].gateType == 2);
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                vertex_descriptor in = (*vi);
                for(j = 0; j < graphIn[ in ].listOfCells.size(); j++){
                        if( j == option )
                                model.add(Xoi[graphIn[ in ].gate_index][j] == 1);
                        else
                                model.add(Xoi[graphIn[ in ].gate_index][j] == 0);
                }
                
        }
        
}

bool loopDetect(CircuitGraph& graphIn){
        
        CircuitGraph::adjacency_iterator adj_vi, adj_vi_end ;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi1, vi1_end ;
        
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
        {
                for (boost::tie(vi1, vi1_end) = boost::vertices(graphIn) ; vi1 != vi1_end ; ++vi1){
                        if(graphIn[ *vi1 ].gateType == 2)
                                graphIn[ *vi1 ].visited = 0;
                        else
                                graphIn[ *vi1 ].visited = 1;
                }
                
                if(graphIn[ *vi ].gateType == 2){
                        
                        int flag;
                        stack<vertex_descriptor> myStack;
                        myStack.push(*vi);
                        //cout << "starting from node " << graphIn[ *vi ].instanceName << endl;

                        while(!myStack.empty()){ 
                                
                                flag = 0;
                                vertex_descriptor in = myStack.top();
                                assert(graphIn[ in ].gateType == 2);
                                //cout << "visiting node " << graphIn[ in ].instanceName << endl;

                                if(in == *vi && myStack.size() > 1){
                                        flag = 2;
                                        break;
                                }
                                
                                for (boost::tie(adj_vi, adj_vi_end) = boost::adjacent_vertices(in, graphIn); 
                                     adj_vi != adj_vi_end ; ++adj_vi){
                                        if(graphIn[*adj_vi].visited == 1)
                                                continue;
                                        
                                        if(graphIn[*adj_vi].gateType == 2)
                                        {
                                                flag = 1;
                                                graphIn[*adj_vi].visited = 1;
                                                myStack.push(*adj_vi);
                                        }
                                }
                                
                                if(flag == 0){
                                        myStack.pop();
                                }
                        }
                        
                        if(flag == 2){
                                cout << "Pure combinational loop found!" << endl;
                                cout << "Node name is " << graphIn[ *vi ].instanceName << endl;
                                return true;
                        }
                }
        }
        
        cout << "no combinational loop found!" << endl;
        return false;
}

bool findLoops(CircuitGraph& graphIn, vertex_descriptor v){
        
        set<vertex_descriptor> final;
        vector<vertex_descriptor> temp;
        vector<vertex_descriptor>::iterator it_vertex;
        CircuitGraph::adjacency_iterator adj_vi, adj_vi_end ;
        CircuitGraph::inv_adjacency_iterator inv_adj_vi, inv_adj_vi_end ;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
        
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                if(graphIn[ *vi ].gateType == 2 || graphIn[ *vi ].gateType == 3)
                        graphIn[ *vi ].visited = 0;
                else
                        graphIn[ *vi ].visited = 1;
        }
                
        for (boost::tie(inv_adj_vi, inv_adj_vi_end) = boost::inv_adjacent_vertices(v, graphIn); inv_adj_vi != inv_adj_vi_end ; ++inv_adj_vi){
                vertex_descriptor in = *inv_adj_vi;
                if(graphIn[ in ].gateType == 3){
                        temp.push_back(in);
                }  
        }
        
        if(temp.size() == 0)
                return false;
        
        int flag;
        stack<vertex_descriptor> myStack;
        myStack.push(v);
        //cout << "starting from node " << graphIn[ vi ].instanceName << endl;
        
        while(!myStack.empty()){ 
                
                flag = 0;
                vertex_descriptor in = myStack.top();
                //cout << "visiting node " << graphIn[ in ].instanceName << endl;
                
                for (boost::tie(adj_vi, adj_vi_end) = boost::adjacent_vertices(in, graphIn); 
                     adj_vi != adj_vi_end ; ++adj_vi){
                        
                        if(graphIn[*adj_vi].visited == 1)
                                continue;
                        
                        if(graphIn[*adj_vi].gateType == 2)
                        {
                                flag = 1;
                                graphIn[*adj_vi].visited = 1;
                                myStack.push(*adj_vi);
                        }
                        else if(graphIn[ *adj_vi ].gateType == 3)
                        {
                                flag = 1;
                                graphIn[*adj_vi].visited = 1;
                                it_vertex = std::find(temp.begin(), temp.end(), *adj_vi);
                                if(it_vertex != temp.end())
                                        final.insert(*adj_vi);
                        }
                }
                
                if(flag == 0){
                        myStack.pop();
                }
        }
        
        if(final.size() == 0)
                return false;
        
        std::swap(graphIn[ v ].loopDFFs, final);
        return true;
}


bool lessArea(const string &s1, const string &s2)
{
        LibParserCellInfo *LPCI1, *LPCI2;
        LPCI1 = &(lpPtr->mapStandardLibraryCells[s1]);
        LPCI2 = &(lpPtr->mapStandardLibraryCells[s2]);
        
        return (LPCI1->area < LPCI2->area);
}


double topoSort(CircuitGraph& graphIn){
        
        vector<vertex_descriptor> fullSet;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
	CircuitGraph::inv_adjacency_iterator in_adj_vi, in_adj_vi_end ;
        
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
        {
                vertex_descriptor in = (*vi);
                if(graphIn[in].gateType == 0){
                        graphIn[in].visited = true;
                        graphIn[in].topo_id = 0;
                }
                else{
                        graphIn[in].visited = false;
                        fullSet.push_back(in);
                }
        }
        
        int i = 1;
        while(fullSet.size()){
                for(int j = 0; j < fullSet.size();){
                        
                        int flag = 0;
                        vertex_descriptor in = fullSet[j];
                        for (boost::tie(in_adj_vi, in_adj_vi_end) = boost::inv_adjacent_vertices(in, graphIn); in_adj_vi != in_adj_vi_end ; ++in_adj_vi){
                                vertex_descriptor fanin = (*in_adj_vi);
                                if(graphIn[fanin].gateType == 0 || graphIn[fanin].gateType == 1)
                                        continue;
                                else if(graphIn[fanin].gateType == 3 && graphIn[in].gateType == 2)
                                        continue;
                                else if(graphIn[fanin].visited == true)
                                        continue;
                                else
                                        flag = 1;
                        }
                        
                        if(flag == 0){
                                graphIn[in].topo_id = i;
                                graphIn[in].visited = true;
                                fullSet.erase(fullSet.begin() + j);
                        }
                        else
                                j++;
                }
                
                i++;
        }        
        
/*
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
        {
                vertex_descriptor in = (*vi);
                cout << "Instnace name is " << graphIn[in].instanceName << endl;
                cout << "Topo id is " << graphIn[in].topo_id << endl;
                
        }
*/
}


void fineroptSelection(CircuitGraph& graphIn, vertex_descriptor in, double* outputLoad){
        
        vec med;
        bool valid, valid1;
        double cur_delay;
        med.x = outputLoad[graphIn[in].gate_index];
        map<string, double> delayRank;
        map<string, double> ::iterator it;
        
        vector<LibParserPinInfo>::iterator it_lbpp;
        vector<LibParserTimingInfo>::iterator it_lbpt;
        //cout << cell.name << "\t" << cell.footprint << endl;
        for(int i = 0; i < graphIn[in].listOfCells.size(); i++){
                int flag = 0;
                double temp = 0.0;
                LibParserCellInfo cell = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[i]];
                
                for(it_lbpp = cell.pins.begin(); it_lbpp != cell.pins.end(); it_lbpp++){
                        if(it_lbpp->name == "o")
                                break;
                }
                
                if(it_lbpp->maxCapacitance < med.x)
                        continue;
                
                for(it_lbpt = cell.timingArcs.begin(); it_lbpt != cell.timingArcs.end(); it_lbpt++){
                        if(it_lbpt->fromPin != "o")
                        {
                                string tmp_name = graphIn[ in ].instanceName + "/" + it_lbpt->fromPin;
                                med.y = max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition);
                                //med.y = max(it_lbpt->fallTransition, it_lbpt->riseTransition);
                                med.z = it_lbpt->fallDelay(med.x, med.y, valid);
                                double outputSlew = it_lbpt->fallTransition(med.x, med.y, valid1);
                                if(outputSlew > lpPtr->default_max_transition){
                                        flag = 1;
                                        break;
                                }

                                if(valid && valid1){
                                        if(temp <= med.z)
                                                temp = med.z;
                                }
                                else
                                        flag = 1;
                        }
                }
                
                if(graphIn[in].listOfCells[i] == graphIn[in].cellName)
                        cur_delay = temp;
                        
                if(flag == 0)
                        delayRank.insert(pair<string, double>(graphIn[in].listOfCells[i], temp));
        }
        
        vector < string > sorted_vec;
        vector < string > :: iterator it_vec;
        sorted_vec = graphIn[ in ].listOfCells;
        std::sort(sorted_vec.begin(), sorted_vec.end(), lessLeakage);
                
        it = delayRank.find(graphIn[ in ].cellName);
        if(it == delayRank.end()){
                graphIn[ in ].candCells.clear();
                for(it_vec = sorted_vec.begin(); it_vec != sorted_vec.end(); it_vec++){
                        if(delayRank.find(*it_vec) != delayRank.end())
                                graphIn[ in ].candCells.push_back(*it_vec);
                        
                        if(graphIn[ in ].candCells.size() == 3)
                                break;
                }
        }
        else
        {
                graphIn[ in ].candCells.clear();
                double delay_bar = (*it).second;
                double area_bar = lpPtr->mapStandardLibraryCells[(*it).first].area;
                for(it = delayRank.begin(); it != delayRank.end(); it++){
                        if((*it).second < delay_bar && fabs(lpPtr->mapStandardLibraryCells[(*it).first].area - area_bar) < 8.1)
                                graphIn[ in ].candCells.push_back((*it).first);
                        
                        if(graphIn[ in ].candCells.size() == 3)
                                break;
                }
                
                if(graphIn[ in ].candCells.size() < 3)
                {
                        for(it = delayRank.begin(); it != delayRank.end(); it++){
                                if( (*it).second >= delay_bar )
                                        if(area_bar >= lpPtr->mapStandardLibraryCells[(*it).first].area 
                                           && (area_bar - lpPtr->mapStandardLibraryCells[(*it).first].area) < 8.1)
                                                graphIn[ in ].candCells.push_back((*it).first);
                                        
                                if(graphIn[ in ].candCells.size() == 3)
                                        break;
                        }
                }
                
                if(graphIn[ in ].candCells.size() < 3)
                {
                        for(it = delayRank.begin(); it != delayRank.end(); it++){
                                if( (*it).second >= delay_bar )
                                        if(area_bar > lpPtr->mapStandardLibraryCells[(*it).first].area 
                                           && (area_bar - lpPtr->mapStandardLibraryCells[(*it).first].area) > 8.1)
                                                graphIn[ in ].candCells.push_back((*it).first);
                                        
                                if(graphIn[ in ].candCells.size() == 3)
                                        break;
                        }
                }        
        }
        
        graphIn[ in ].isFixed = false;
        assert(graphIn[ in ].candCells.size() == 3);
}


void rangeFixing(CircuitGraph& graphIn, vertex_descriptor in){
        CircuitGraph::adjacency_iterator adj_vi, adj_vi_end ;
        CircuitGraph::inv_adjacency_iterator inv_adj_vi, inv_adj_vi_end ;
        
        for (boost::tie(adj_vi, adj_vi_end) = boost::adjacent_vertices(in, graphIn); adj_vi != adj_vi_end ; ++adj_vi){
                if(graphIn[*adj_vi].gateType == 2){
                        graphIn[*adj_vi].isFixed = true;
                        graphIn[*adj_vi].candCells.clear();
                        graphIn[*adj_vi].candCells.push_back(graphIn[*adj_vi].cellName);
                }
        }
}


void picksizingCandidates(CircuitGraph& graphIn, double* outputLdArray){
        
        multimap<double, vertex_descriptor> vioRank;
        multimap<double, vertex_descriptor> :: reverse_iterator rit_map;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
        
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                if(graphIn[ *vi ].gateType == 2 && graphIn[ *vi ].Slack_Violation >= 0.0001)
                        vioRank.insert(pair<double, vertex_descriptor> (graphIn[ *vi ].Slack_Violation, *vi));
                if(graphIn[ *vi ].gateType == 2){
                        //graphIn[ *vi ].candCells.clear();
                        //graphIn[ *vi ].candCells.push_back(graphIn[ *vi ].cellName);
                        graphIn[ *vi ].isFixed = false;
                }
        }
        
        long gate_cnt = 0;
        int topo_threashold = opt_rd;
        const int gate_cnt_threshold = 50;
        
        assert(opt_rd > 0);
        for(rit_map = vioRank.rbegin(); rit_map != vioRank.rend(); rit_map++)
        {
                if(graphIn[(*rit_map).second].topo_id > topo_threashold || graphIn[(*rit_map).second].wasFixed == false){
                        graphIn[(*rit_map).second].candCells.clear();
                        graphIn[(*rit_map).second].candCells.push_back(graphIn[(*rit_map).second].cellName);
                        graphIn[(*rit_map).second].isFixed = true;
                        continue;
                }
                else if(graphIn[(*rit_map).second].isFixed == false){
                        //else{
                        //graphIn[(*rit_map).second].isFixed = false;
                        cout << "optimized gate name is " << graphIn[(*rit_map).second].instanceName << endl;
                        rangeFixing(graphIn, (*rit_map).second);
                        fineroptSelection(graphIn, (*rit_map).second, outputLdArray);
                        gate_cnt++;
                }
                
                if(gate_cnt >= gate_cnt_threshold)
                        break;
        }
        
        if(gate_cnt == 0){
        //if(gate_cnt < gate_cnt_threshold * 0.2){
                for(rit_map = vioRank.rbegin(); rit_map != vioRank.rend(); rit_map++)
                {
                        graphIn[(*rit_map).second].isFixed = false;
                        cout << "Zero Set added, optimized gate name is " << graphIn[(*rit_map).second].instanceName << endl;
                        fineroptSelection(graphIn, (*rit_map).second, outputLdArray);
                        gate_cnt++;
                        if(gate_cnt >= gate_cnt_threshold * 0.2)
                                break;
                }
        }
        
        
        //note that gate_cnt could be less than gate_cnt_threashold
}



double nodeVioCheck(CircuitGraph& graphIn, vertex_descriptor in){
        
        assert(graphIn[ in ].gateType == 2);
        LibParserCellInfo *LPCI;
        vector<LibParserPinInfo>::iterator it;
        double temp_slew_violation = 0.0, temp_slack_violation = 0.0, temp_slack_redundancy = 0.0;
        LPCI = &(lpPtr->mapStandardLibraryCells[ graphIn[ in ].cellName ]);
        for(it = LPCI->pins.begin(); it != LPCI->pins.end(); it++)
        {
                if(it->name != "o")//not the output pins
                {
                        string tmp_name = graphIn[ in ].instanceName + "/" + it->name;
                        if(tpPtr->PinsTiming[tmp_name].riseTransition > lpPtr->default_max_transition)
                                temp_slew_violation += fabs(tpPtr->PinsTiming[tmp_name].riseTransition - lpPtr->default_max_transition);
                                
                        if(tpPtr->PinsTiming[tmp_name].fallTransition > lpPtr->default_max_transition)
                                temp_slew_violation += fabs(tpPtr->PinsTiming[tmp_name].fallTransition - lpPtr->default_max_transition);
                        
                        if(tpPtr->PinsTiming[tmp_name].riseSlack < 0)
                                temp_slack_violation += fabs(tpPtr->PinsTiming[tmp_name].riseSlack);
                        else
                                temp_slack_redundancy += fabs(tpPtr->PinsTiming[tmp_name].riseSlack);
                        
                        if(tpPtr->PinsTiming[tmp_name].fallSlack < 0)
                                temp_slack_violation += fabs(tpPtr->PinsTiming[tmp_name].fallSlack);
                        else
                                temp_slack_redundancy += fabs(tpPtr->PinsTiming[tmp_name].fallSlack);
                }
        }
        
        return temp_slack_violation;
}


bool postProcessing(CircuitGraph& graphIn, vertex_descriptor in){
        
        assert(graphIn[ in ].gateType == 2);        
        LibParserCellInfo *LPCI;
        vector<LibParserPinInfo>::iterator it;
        double temp_slew_violation = 0.0, temp_slack_violation = 0.0, temp_slack_redundancy = 0.0;
        LPCI = &(lpPtr->mapStandardLibraryCells[ graphIn[ in ].cellName ]);
        for(it = LPCI->pins.begin(); it != LPCI->pins.end(); it++)
        {
                if(it->name != "o")//not the output pins
                {
                        string tmp_name = graphIn[ in ].instanceName + "/" + it->name;
                        if(tpPtr->PinsTiming[tmp_name].riseTransition > lpPtr->default_max_transition)
                                temp_slew_violation += fabs(tpPtr->PinsTiming[tmp_name].riseTransition - lpPtr->default_max_transition);
                                
                        if(tpPtr->PinsTiming[tmp_name].fallTransition > lpPtr->default_max_transition)
                                temp_slew_violation += fabs(tpPtr->PinsTiming[tmp_name].fallTransition - lpPtr->default_max_transition);
                        
                        if(tpPtr->PinsTiming[tmp_name].riseSlack < 0)
                                temp_slack_violation += fabs(tpPtr->PinsTiming[tmp_name].riseSlack);
                        else
                                temp_slack_redundancy += fabs(tpPtr->PinsTiming[tmp_name].riseSlack);
                        
                        if(tpPtr->PinsTiming[tmp_name].fallSlack < 0)
                                temp_slack_violation += fabs(tpPtr->PinsTiming[tmp_name].fallSlack);
                        else
                                temp_slack_redundancy += fabs(tpPtr->PinsTiming[tmp_name].fallSlack);
                }
        }
        
        vector < string > sorted_vec;
        vector < string > :: iterator it_vec;
        sorted_vec = graphIn[ in ].listOfCells;
        std::sort(sorted_vec.begin(), sorted_vec.end(), lessLeakage);
        //std::sort(sorted_vec.begin(), sorted_vec.end(), lessArea);// should be better
        /*
        for(it_vec = sorted_vec.begin(); it_vec != sorted_vec.end(); it_vec++){
                cout << lpPtr->mapStandardLibraryCells[ *it_vec ].area << "\t";
                cout << lpPtr->mapStandardLibraryCells[ *it_vec ].leakagePower << endl;
                }
        cin.get();
        */
        it_vec = std::find(sorted_vec.begin(), sorted_vec.end(), graphIn[ in ].cellName);
        assert(it_vec != sorted_vec.end());
        int pos = distance(sorted_vec.begin(), it_vec);
        
        //if(temp_slack_violation >= 50.0){//larger size
        if(pos != sorted_vec.size() - 1)
                pos++;
                
        graphIn[ in ].cellName = sorted_vec[pos];
        //cout << "hoho" << endl;
        //return true;
//}
        //else if(temp_slack_violation <= 0.00001 && temp_slack_redundancy > 400.0){//smaller size
        //else if(temp_slack_violation <= 0.00001){//smaller size
        if(pos != 0)
                pos--;
                
                //graphIn[ in ].cellName = sorted_vec[pos];
                //cout << "hehe" << endl;
                //return true;
//}
        
        return false;
}


void areaSampler(CircuitGraph& graphIn, vertex_descriptor v){
        assert(graphIn[ v ].gateType == 2);
        
        vector < string > sorted_vec;
        vector < string > :: iterator it_vec;
        sorted_vec = graphIn[ v ].listOfCells;
        sort(sorted_vec.begin(), sorted_vec.end(), lessArea);
        if(opt_rd == 0)
        {
                graphIn[ v ].candCells.clear();
                graphIn[ v ].candCells.push_back(sorted_vec[0]);
                graphIn[ v ].candCells.push_back(sorted_vec[5]);
                graphIn[ v ].candCells.push_back(sorted_vec[11]);
                graphIn[ v ].candCells.push_back(sorted_vec[17]);
                graphIn[ v ].candCells.push_back(sorted_vec[23]);
                graphIn[ v ].candCells.push_back(sorted_vec[29]);
        }
        else{
                assert(graphIn[ v ].cellName ==  lpPtr->mapStandardLibraryCells[graphIn[ v ].cellName].name);
                it_vec = std::find(sorted_vec.begin(), sorted_vec.end(), graphIn[ v ].cellName);
                assert(it_vec != sorted_vec.end());
                int start_pos, end_pos;
                int med_pos = distance(sorted_vec.begin(), it_vec);
                if(med_pos == 0){
                        start_pos = 0;
                        med_pos = graphIn[ v ].opt_radius;
                        end_pos = 2 * graphIn[ v ].opt_radius;
                }
                else if(med_pos == sorted_vec.size() - 1){
                        start_pos = sorted_vec.size() - 1 - 2 * graphIn[ v ].opt_radius;
                        med_pos = sorted_vec.size() - 1 - graphIn[ v ].opt_radius;
                        end_pos = sorted_vec.size() - 1;
                }
                else{
                        start_pos = max(0, med_pos - graphIn[ v ].opt_radius);
                        end_pos = min((int)(sorted_vec.size() - 1), med_pos + graphIn[ v ].opt_radius);
                }
                
                if(end_pos - start_pos + 1 > largestNumOption)
                        largestNumOption = end_pos - start_pos + 1;
                
                //if the candidate set is not changed from the previous round
                vector<string> temp;
                for(int i = start_pos; i <= end_pos; i++)
                        temp.push_back(sorted_vec[i]);
                
                if(graphIn[ v ].candCells.front() == temp.front() && graphIn[ v ].candCells.back() == temp.back())// consecutive region required
                        if(graphIn[ v ].opt_radius > 1)
                                graphIn[ v ].opt_radius--;
                
                std::swap(graphIn[ v ].candCells, temp);
                //assert(graphIn[ v ].candCells.size() == 3);
                it_vec = std::find(graphIn[ v ].candCells.begin(), graphIn[ v ].candCells.end(), graphIn[ v ].cellName);
                assert(it_vec != graphIn[ v ].candCells.end());
        }
        
        assert(graphIn[ v ].candCells.size() != 0);
}

void fixedSampler(CircuitGraph& graphIn, vertex_descriptor v){
        
        assert(graphIn[ v ].gateType == 2);
        assert(sizeVec.size() == 10);
        
        string temp;
        assert(graphIn[ v ].cellName ==  lpPtr->mapStandardLibraryCells[graphIn[ v ].cellName].name);
        string cellType = graphIn[ v ].cellName.substr(0, 4);
        vector<string> tmpContainer;
        temp = cellType + "s" + sizeVec[2];
        tmpContainer.push_back(temp);
        temp = cellType + "s" + sizeVec[4];
        tmpContainer.push_back(temp);
        temp = cellType + "s" + sizeVec[6];
        tmpContainer.push_back(temp);
        temp = cellType + "m" + sizeVec[2];
        tmpContainer.push_back(temp);
        temp = cellType + "m" + sizeVec[4];
        tmpContainer.push_back(temp);
        temp = cellType + "m" + sizeVec[6];
        tmpContainer.push_back(temp);
        temp = cellType + "f" + sizeVec[2];
        tmpContainer.push_back(temp);
        temp = cellType + "f" + sizeVec[4];
        tmpContainer.push_back(temp);
        temp = cellType + "f" + sizeVec[6];
        tmpContainer.push_back(temp);
        std::swap(graphIn[ v ].candCells, tmpContainer);        
        assert(graphIn[ v ].candCells.size() == 9);
}

void uniformSampler(CircuitGraph& graphIn, vertex_descriptor v){
        
        assert(graphIn[ v ].gateType == 2);
        assert(sizeVec.size() == 10);
        
        string temp;
        vector < string > sorted_vec;
        sorted_vec = graphIn[ v ].listOfCells;
        sort(sorted_vec.begin(), sorted_vec.end(), lessArea);
        if(opt_rd == 0)
        {
                string cellType = graphIn[ v ].cellName.substr(0, 4);
                vector<string> tmpContainer;
                temp = cellType + "s" + sizeVec[2];
                tmpContainer.push_back(temp);
                temp = cellType + "s" + sizeVec[4];
                tmpContainer.push_back(temp);
                temp = cellType + "s" + sizeVec[5];
                tmpContainer.push_back(temp);
                temp = cellType + "s" + sizeVec[6];
                tmpContainer.push_back(temp);
                temp = cellType + "m" + sizeVec[2];
                tmpContainer.push_back(temp);
                temp = cellType + "m" + sizeVec[4];
                tmpContainer.push_back(temp);
                temp = cellType + "m" + sizeVec[5];
                tmpContainer.push_back(temp);
                temp = cellType + "m" + sizeVec[6];
                tmpContainer.push_back(temp);
                temp = cellType + "f" + sizeVec[2];
                tmpContainer.push_back(temp);
                temp = cellType + "f" + sizeVec[4];
                tmpContainer.push_back(temp);
                temp = cellType + "f" + sizeVec[5];
                tmpContainer.push_back(temp);
                temp = cellType + "f" + sizeVec[6];
                tmpContainer.push_back(temp);
                std::swap(graphIn[ v ].candCells, tmpContainer);        
        }
        else{
                assert(graphIn[ v ].cellName ==  lpPtr->mapStandardLibraryCells[graphIn[ v ].cellName].name);
                string cellType = graphIn[ v ].cellName.substr(0, 4);
                string cellSize = graphIn[ v ].cellName.substr(5, 2);
                
                vector<string> tmpContainer;
                vector<string>::iterator it_string;
                it_string = std::find(sizeVec.begin(), sizeVec.end(), cellSize);
                assert(it_string != sizeVec.end());
                int loc = distance(sizeVec.begin(), it_string);
                if(loc == 0){
                        temp = cellType + "s" + sizeVec[0];
                        tmpContainer.push_back(temp);
                        temp = cellType + "s" + sizeVec[0 + graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "s" + sizeVec[0 + 2 * graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "s" + sizeVec[1 + graphIn[ v ].opt_radius];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                temp = cellType + "s" + sizeVec[1 + 2 * graphIn[ v ].opt_radius];
                                tmpContainer.push_back(temp);
                        }
                        
                        temp = cellType + "m" + sizeVec[0];
                        tmpContainer.push_back(temp);
                        temp = cellType + "m" + sizeVec[0 + graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "m" + sizeVec[0 + 2 * graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "m" + sizeVec[1 + graphIn[ v ].opt_radius];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                temp = cellType + "m" + sizeVec[1 + 2 * graphIn[ v ].opt_radius];
                                tmpContainer.push_back(temp);
                        }
                        
                        temp = cellType + "f" + sizeVec[0];
                        tmpContainer.push_back(temp);
                        temp = cellType + "f" + sizeVec[0 + graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "f" + sizeVec[0 + 2 * graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "f" + sizeVec[1 + graphIn[ v ].opt_radius];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                temp = cellType + "f" + sizeVec[1 + 2 * graphIn[ v ].opt_radius];
                                tmpContainer.push_back(temp);
                        }
                        
                }
                else if(loc == 9){
                        temp = cellType + "s" + sizeVec[9 - 2 * graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "s" + sizeVec[9 - graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "s" + sizeVec[9];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "s" + sizeVec[9 - 2 * graphIn[ v ].opt_radius + 1];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                temp = cellType + "s" + sizeVec[9 - 2 * graphIn[ v ].opt_radius - 1];
                                tmpContainer.push_back(temp);
                        }
                        
                        temp = cellType + "m" + sizeVec[9 - 2 * graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "m" + sizeVec[9 - graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "m" + sizeVec[9];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "m" + sizeVec[9 - 2 * graphIn[ v ].opt_radius + 1];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                temp = cellType + "m" + sizeVec[9 - 2 * graphIn[ v ].opt_radius - 1];
                                tmpContainer.push_back(temp);
                        }
                        
                        temp = cellType + "f" + sizeVec[9 - 2 * graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "f" + sizeVec[9 - graphIn[ v ].opt_radius];
                        tmpContainer.push_back(temp);
                        temp = cellType + "f" + sizeVec[9];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "f" + sizeVec[9 - 2 * graphIn[ v ].opt_radius + 1];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                temp = cellType + "f" + sizeVec[9 - 2 * graphIn[ v ].opt_radius - 1];
                                tmpContainer.push_back(temp);
                        }
                        
                }
                else{
                        int start = max(0, loc - graphIn[ v ].opt_radius);
                        int end = min(9, loc + graphIn[ v ].opt_radius);
                        temp = cellType + "s" + sizeVec[start];
                        tmpContainer.push_back(temp);
                        temp = cellType + "s" + sizeVec[loc];
                        tmpContainer.push_back(temp);
                        temp = cellType + "s" + sizeVec[end];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "s" + sizeVec[loc + 1];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                int flag = 0;
                                if(start > 0 && flag == 0){
                                        temp = cellType + "s" + sizeVec[start - 1];
                                        tmpContainer.push_back(temp);
                                        flag = 1;
                                }
                                if(end < 9 && flag == 0){
                                        temp = cellType + "s" + sizeVec[end + 1];
                                        tmpContainer.push_back(temp);
                                }
                        }
                        
                        temp = cellType + "m" + sizeVec[start];
                        tmpContainer.push_back(temp);
                        temp = cellType + "m" + sizeVec[loc];
                        tmpContainer.push_back(temp);
                        temp = cellType + "m" + sizeVec[end];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "m" + sizeVec[loc + 1];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                int flag = 0;
                                if(start > 0 && flag == 0){
                                        temp = cellType + "m" + sizeVec[start - 1];
                                        tmpContainer.push_back(temp);
                                        flag = 1;
                                }
                                if(end < 9 && flag == 0){
                                        temp = cellType + "m" + sizeVec[end + 1];
                                        tmpContainer.push_back(temp);
                                }
                        }
                        
                        temp = cellType + "f" + sizeVec[start];
                        tmpContainer.push_back(temp);
                        temp = cellType + "f" + sizeVec[loc];
                        tmpContainer.push_back(temp);
                        temp = cellType + "f" + sizeVec[end];
                        tmpContainer.push_back(temp);
                        
                        if(graphIn[ v ].opt_radius != 1){
                                temp = cellType + "f" + sizeVec[loc + 1];
                                tmpContainer.push_back(temp);
                        }
                        else
                        {
                                int flag = 0;
                                if(start > 0 && flag == 0){
                                        temp = cellType + "f" + sizeVec[start - 1];
                                        tmpContainer.push_back(temp);
                                        flag = 1;
                                }
                                if(end < 9 && flag == 0){
                                        temp = cellType + "f" + sizeVec[end + 1];
                                        tmpContainer.push_back(temp);
                                }
                        }
                        
                }
                
                if(graphIn[ v ].candCells.front() == tmpContainer.front() && graphIn[ v ].candCells.back() == tmpContainer.back()){
                        if(graphIn[ v ].opt_radius > 1)
                                graphIn[ v ].opt_radius--;
                        if(graphIn[ v ].lut_radius > 1)
                                graphIn[ v ].lut_radius--;
                }
                std::swap(graphIn[ v ].candCells, tmpContainer);        
        }
        
        assert(graphIn[ v ].candCells.size() == 6 || graphIn[ v ].candCells.size() == 9 || graphIn[ v ].candCells.size() == 12);
}


int solver(CircuitGraph& graphIn, double* outputLdArray, ExtendedVerilogParser* vpPtr, ExtendedSdcParser* sdcPtr, ExtendedLibParser* lpPtr) {
        
        //checkApproxFuntions(graphIn);
        //loopDetect(graphIn);
        //return 1;
        
        unsigned long i, j, k;
        IloEnv env;
        time_t start,end;
        //time (&start);
        
        //define variables.........................
        IloModel model(env);

        unsigned int numOptions;
#ifndef REDUCED_OPTIONS
        numOptions = 30;
#else
        if(opt_rd == 0)
                numOptions = 3;
        else
                numOptions = 3;
#endif
        cout << "Num of options is " << numOptions << endl;
        unsigned long numComGates;
        unsigned long numGates;
        numGates = effGateCnt;
        //unsigned long numGates = boost::num_vertices(graphIn);
        cout << "number of gates considered in the formualtion is " << numGates << endl;
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
	boost::graph_traits<CircuitGraph>::out_edge_iterator oi, oi_end;
        /*
        long count = 0;
        long count_dff = 0;
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
        {
                vertex_descriptor in = (*vi);
                if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                        count++;
                
                if(graphIn[ in ].gateType == 3)
                        count_dff++;
        }
        numGates -= count;
        //numComGates = numGates - count_dff;
        numComGates = numGates;
        */

        /*-------------define decision variables--------------*/
        //define yi
        //Output capacitance of node i
        IloNumVarArray yi(env, numGates, 0, +IloInfinity, ILOFLOAT);
        
        //define Xoi
        //Options for a gate, o for option, i for gate
        NumVarMatrix Xoi(env, numGates);
        for (i = 0; i < numGates; i++) 
#ifndef LIN_RELAX
                Xoi[i] = IloNumVarArray(env, numOptions, 0, 1, ILOINT);
#else
                Xoi[i] = IloNumVarArray(env, numOptions, 0.0, 1.0, ILOFLOAT);
#endif   
        //define Si (slew)  For the output of a gate
        IloNumVarArray Si(env, numGates, 0.0, +IloInfinity, ILOFLOAT);
        //define Di (delay)  For the output of a gate
        IloNumVarArray Di(env, numGates, 0.0, +IloInfinity, ILOFLOAT);
        IloNumVarArray DBi(env, numGates, 0.0, +100.0, ILOFLOAT);
        IloNumVarArray SBi(env, numGates, 0.0, +20.0, ILOFLOAT);

        //make a vector to store all the gate sizes
        sizeVec.clear();
        sizeVec.push_back("01");
        sizeVec.push_back("02");
        sizeVec.push_back("03");
        sizeVec.push_back("04");
        sizeVec.push_back("06");
        sizeVec.push_back("08");
        sizeVec.push_back("10");
        sizeVec.push_back("20");
        sizeVec.push_back("40");
        sizeVec.push_back("80");
        
        //fix DFF options + initilization of several parameters
        picksizingCandidates(graphIn, outputLdArray);
        i = 0;
        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
        {
                vertex_descriptor in = (*vi);
                if(graphIn[ in ].gateType == 3)
                {
                        model.add(Xoi[i][0] == 1);
                        for(j = 1; j < numOptions; j++)
                                model.add(Xoi[i][j] == 0);
                        
                        //model.add(DBi[i] == 0.0);
                        i++;
                }
                
                if(graphIn[ in ].gateType == 2){
                        //findLoops(graphIn, in);
                        //if(graphIn[ in ].opt_radius == 1 && graphIn[ in ].lut_radius > 1)
                        //if(opt_rd > 0 && graphIn[ in ].lut_radius > 1)
                        //graphIn[ in ].lut_radius--;
#ifdef REDUCED_OPTIONS
                        //fixedSampler(graphIn, in);
                        //uniformSampler(graphIn, in);
                        //areaSampler(graphIn, in);
                        if(graphIn[ in ].isFixed == true){
                                assert(graphIn[ in ].candCells.size() == 1);
                                model.add(Xoi[i][0] == 1);
                                for(j = 1; j < numOptions; j++)
                                        model.add(Xoi[i][j] == 0);
                        }
#endif
                        //cout << graphIn[ in ].instanceName << " 's loop DFFs are" << endl;
                        //set<vertex_descriptor>::iterator it_set;
                        //for(it_set = graphIn[ in ].loopDFFs.begin(); it_set != graphIn[ in ].loopDFFs.end(); it_set++)
                        //{
                        //cout << graphIn[ *it_set ].instanceName << "\t";
                        //}
                        //cout << endl;
                        i++;
                }
        }
        
        //cout << "Finish searching high fanout node!" << endl;
        
        //const double M = numeric_limits<double>::max();
        const double M = 410.0;//max is 407
        string inputPin, outputPin;
        double inputDelay, inputSlew, outputLoad;
        vector<netConnectivity>::iterator it_vec;
        vector<vertex_descriptor>::iterator it_vertex;
        vector<LibParserPinInfo>::iterator it_pin;
        
        CircuitGraph::adjacency_iterator adj_vi, adj_vi_end ;
        CircuitGraph::inv_adjacency_iterator in_adj_vi, in_adj_vi_end ;
        
        //add object, constraints and solve
        try{
                //add objective expression
                IloExpr Power(env);
                IloExpr Slew(env);
                IloExpr Delay(env);
                std::vector<std::string> cellNames;
                
/*
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1){
                                graphIn[ in ].gate_index = -1;
                                continue;
                        }
                        else{
                                graphIn[ in ].gate_index = i;
                                i++;
                        }
                }
*/              
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
                {       
                        vertex_descriptor in = (*vi);
                        //cout << "the name of the instance is " << graphIn[ in ].instanceName << endl;
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        //cout << "the name of the instance is " << graphIn[ in ].instanceName << endl;
                        //assert(graphIn[ in ].listOfCells.size() == numOptions);
                        
                        if(graphIn[ in ].gateType == 2){
                                for(j = 0; j < numOptions; j++)
                                {
                                        //cout << graphIn[ in ].listOfCells[j] << endl;
#ifndef REDUCED_OPTIONS
                                        Power += lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].leakagePower * Xoi[i][j];
#else
                                        if(j == graphIn[ in ].candCells.size())
                                                break;
                                        Power += lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].leakagePower * Xoi[i][j];
#endif
                                }
                                
                                for (boost::tie(adj_vi, adj_vi_end) = boost::adjacent_vertices(in, graphIn); adj_vi != adj_vi_end ; ++adj_vi){
                                        if(graphIn[*adj_vi].gateType == 1)
                                        {
                                                Delay += DBi[i];
                                                break;
                                        }
                                }
                        }
                        
                        Slew += Si[i];
                        if(graphIn[ in ].gateType == 3)
                                Delay += DBi[i];
                        
                        i++;
                }
                
                //IloObjective obj = IloMinimize(env, Power);
                //IloObjective obj = IloMinimize(env, Power + 2 * Delay);
                IloObjective obj = IloMinimize(env, Delay);
                //IloObjective obj = IloMinimize(env, Power + 2 * Slew);
                //IloObjective obj = IloMinimize(env, Slew);
                model.add(obj); 
                Power.end();
                Delay.end();
                Slew.end();
                
                /*
                //---------------Min_The fixing---------------stage------------------------------
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
                {
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 2 && boost::out_degree(in, graphIn) <=3){
                                fixVariable(graphIn, Xoi, model, in, 20);
                                i++;
                        }
                }
                
                cout << "Fixed gate num is " << i << endl;
                //-------------------------------------------------------------------------------
                */

                
                //------------------------------------------------adding constraints-------------------------------------------------
                
                //sum Xoi equals to 1
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
                {
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        IloExpr expr1(env,0);
                        if(graphIn[ in ].gateType == 2 && graphIn[ in ].isFixed == false)
                                for(j = 0; j < numOptions; j++)
                                        expr1 += Xoi[i][j]; 
                        
                        if(graphIn[ in ].gateType == 2 && graphIn[ in ].isFixed == false)
                                model.add(expr1 == 1);
                        
                        expr1.end();
                        i++;
                }
                
                
                //yi equals to summation of the output loading
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
                {
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        IloExpr expr1(env,0);
                        outputPin = "o";
                        //cout << "look here " << graphIn[ in ].mapPinNetDirection[outputPin].first << endl;
                        //cout << "Now connectivity size is " << vpPtr->verilogConnectivity.size() << endl;
                        
                        for(it_vec = vpPtr->verilogConnectivity.begin(); it_vec != vpPtr->verilogConnectivity.end(); it_vec++)
                        {
                                if(it_vec->netName == graphIn[ in ].mapPinNetDirection[outputPin].first)
                                        break;
                        }
                        //if(it_vec == vpPtr->verilogConnectivity.end())
                        //continue;

                        assert(it_vec != vpPtr->verilogConnectivity.end());
                        
                        k = 0;
                        netConnectivity myNet = vpPtr->verilogConnectivity[distance(vpPtr->verilogConnectivity.begin(), it_vec)];
                        
                        assert(boost::out_degree(in, graphIn) > 0);
                        for (boost::tie(oi, oi_end) = boost::out_edges(in, graphIn) ; oi != oi_end ; ++oi )
                        {
                                //cout << "Net parasitic load is " << graphIn[(*oi)].netParasiticLoad << endl;
                                expr1 += graphIn[(*oi)].netParasiticLoad;
                                break;
                        }
                        
                        //cout << "Kernal vertex name is " << graphIn[ in ].instanceName << endl;
                        //cout << "Net name is " << myNet.netName << endl;
                        for(it_vertex = myNet.targetVertices.begin(); it_vertex != myNet.targetVertices.end(); it_vertex++)
                        {
                                //if(graphIn[ *it_vertex ].footPrint == "PI")
                                //continue;
                                
                                assert(graphIn[ *it_vertex ].gateType != 0);
                                //cout << "vertex id is " << *it_vertex << endl;
                                //cout << "vertex name is " << graphIn[ *it_vertex ].instanceName << "\t";
                                //cout << "vertex index is " << graphIn[ *it_vertex ].gate_index << endl;
                                //check for this case in netconnectivity
                                if(graphIn[ *it_vertex ].gateType == 1)
                                {
                                        outputLoad = sdcPtr->outputLoads[graphIn[ *it_vertex ].instanceName];
                                        assert(outputLoad == 4.0);
                                        //cout << "PO load is " << outputLoad << endl;
                                        expr1 += outputLoad; 
                                }
                                else if(graphIn[ *it_vertex ].gateType == 2)
                                {
                                        for(j = 0; j < numOptions; j++){
                                                //cout << lpPtr->mapStandardLibraryCells[graphIn[ *it_vertex ].listOfCells[j]].pins.size() << endl;
#ifndef REDUCED_OPTIONS
                                                for(it_pin = lpPtr->mapStandardLibraryCells[graphIn[ *it_vertex ].listOfCells[j]].pins.begin();
                                                    it_pin != lpPtr->mapStandardLibraryCells[graphIn[ *it_vertex ].listOfCells[j]].pins.end(); it_pin++)
                                                        if(it_pin->name == myNet.targetPins[k]){
                                                                expr1 += it_pin->capacitance * Xoi[graphIn[ *it_vertex ].gate_index][j];
                                                                //cout << "load is " << it_pin->capacitance << endl;
                                                                break;
                                                        }
                                                assert(it_pin != lpPtr->mapStandardLibraryCells[graphIn[ *it_vertex ].listOfCells[j]].pins.end());
#else
                                                if(j == graphIn[ *it_vertex ].candCells.size())
                                                        break;
                                                
                                                for(it_pin = lpPtr->mapStandardLibraryCells[graphIn[ *it_vertex ].candCells[j]].pins.begin();
                                                    it_pin != lpPtr->mapStandardLibraryCells[graphIn[ *it_vertex ].candCells[j]].pins.end(); it_pin++)
                                                        if(it_pin->name == myNet.targetPins[k]){
                                                                expr1 += it_pin->capacitance * Xoi[graphIn[ *it_vertex ].gate_index][j];
                                                                //cout << "load is " << it_pin->capacitance << endl;
                                                                break;
                                                        }
                                                assert(it_pin != lpPtr->mapStandardLibraryCells[graphIn[ *it_vertex ].candCells[j]].pins.end());
#endif
                                        }
                                }
                                else if(graphIn[ *it_vertex ].gateType == 3){
                                        //cout << "DFF load is 0.0" << endl;
                                        expr1 += 0.0; 
                                }
                                
                                k++;
                        }
                        
                        model.add(yi[i] == expr1);
                        expr1.end();
                        i++;        
                }
                
                
                //slew constraint  Si >= Approximated Expression
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                        
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        //cout << "check " << graphIn[ in ].instanceName << endl;
                        for(it_pin = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].pins.begin();
                            it_pin != lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].pins.end(); it_pin++)
                        {
                                if(it_pin->name == "o")
                                        continue;
                                
                                inputPin = it_pin->name;
                                for(it_vec = vpPtr->verilogConnectivity.begin(); it_vec != vpPtr->verilogConnectivity.end(); it_vec++)
                                        if(it_vec->netName == graphIn[ in ].mapPinNetDirection[inputPin].first)
                                                break;
                                
                                assert(it_vec != vpPtr->verilogConnectivity.end());
                                netConnectivity myNet = vpPtr->verilogConnectivity[distance(vpPtr->verilogConnectivity.begin(), it_vec)];
                                
                                //cout << "_look " << graphIn[ myNet.sourceVertex ].gate_index << endl;
                                //cout << "_look2 " << graphIn[ myNet.sourceVertex ].instanceName << endl;
                                //cout << "_look3 " << graphIn[ myNet.sourceVertex ].gateType << endl;        
                                
                                if(myNet.sourceVertex == in)
                                        continue;
                                
                                if(graphIn[ myNet.sourceVertex ].gateType == 3 && graphIn[ in ].loopDFFs.find(myNet.sourceVertex) != graphIn[ in ].loopDFFs.end())
                                {
                                        //cout << "Happen once!" << endl;
                                        //continue; //slew should be add anyway
                                }
                                
                                if(graphIn[ in ].gateType == 2){
                                        
                                        for(j = 0; j < numOptions; j++){
                                                
                                                window w0;
                                                int flag = 0;
                                                bool isValid = true;
                                                IloExpr expr1(env,0);
                                                IloExpr expr2(env,0);
                                                IloExpr expr3(env,0);
                                                vector<vec> approxTerms;
                                                
                                                if(opt_rd == 0){
#ifndef REDUCED_OPTIONS
                                                        
#ifndef RADIUS_BASED 
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, w0, 
                                                                                       0.0, 0.0, 1, isValid);
#else
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                       0.0, 0.0, 1, isValid);
#endif

#else
                                                        if(j == graphIn[ in ].candCells.size())
                                                                break;
#ifndef RADIUS_BASED 
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, w0, 
                                                                                       0.0, 0.0, 1, isValid);
#else
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                       0.0, 0.0, 1, isValid);
#endif
                                                        
#endif
                                                        //cout << "Instance name is " << graphIn[ in ].instanceName << " Slew" << endl;
                                                        //cout << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                                        //cout << approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                                }
                                                else{
                                                        window w;
                                                        string tmp_name = graphIn[ in ].instanceName + "/" + inputPin;
#ifdef ZOOM_IN
#ifndef REDUCED_OPTIONS
                                                        w = lutZoomin(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], graphIn[ in ].instanceName, inputPin, outputLdArray[i], isValid);
#else
                                                        if(j == graphIn[ in ].candCells.size())
                                                                break;
                                                        w = lutZoomin(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], graphIn[ in ].instanceName, inputPin, outputLdArray[i], isValid);
#endif
#endif
                                                        /*
                                                        if(w.x_width == 0){
                                                        cout << "Check here! Slew" << graphIn[ in ].instanceName << "\t" <<
                                                                        lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].footprint << endl;
                                                                        
                                                                cout << "loading is " << outputLdArray[i] << endl;
                                                                cin.get();
                                                                }
                                                        */
                                                        assert(tpPtr->PinsTiming.find(tmp_name) != tpPtr->PinsTiming.end());
                                                        if(isValid == true){
#ifndef REDUCED_OPTIONS

#ifndef RADIUS_BASED 
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, w, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 1, isValid);
#else
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 1, isValid);
#endif

#else
                                                                if(j == graphIn[ in ].candCells.size())
                                                                        break;
                                                                
                                                                //cout << "Instance name is " << graphIn[ in ].instanceName << " Slew" << endl;
                                                                //cout << "check here " << max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition) << endl;
                                                                //cout << "check loading " << outputLdArray[i] << endl;
#ifndef RADIUS_BASED 
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, w, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 1, isValid);
#else
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 1, isValid);
#endif

#endif
                                                                //if(isValid == true){
                                                                //cout << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                                                //cout << approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                                                //}
                                                        }
                                                }
                                                
                                                if(graphIn[ myNet.sourceVertex ].gateType == 0){
                                                        flag = 1;
                                                        inputSlew = sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransFall;
                                                        //cout << "inputSlew " << inputSlew << endl;
                                                        assert(inputSlew == sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransRise);
                                                        assert(inputSlew == 80.0);
                                                        //cout << "inputSlew is " << inputSlew << endl;
                                                        if(isValid == true){
                                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * inputSlew + approxTerms[0].z;
                                                                expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * inputSlew + approxTerms[1].z;
#ifdef MORE_PLANE                
                                                                expr3 += approxTerms[2].x * yi[i] + approxTerms[2].y * inputSlew + approxTerms[2].z;
#endif
                                                        }
                                                }
                                                else if(graphIn[ myNet.sourceVertex ].gateType == 2 || graphIn[ myNet.sourceVertex ].gateType == 3){
                                                        flag = 1;
                                                        //cout << "Hey " << graphIn[ myNet.sourceVertex ].gate_index << endl;
                                                        //cout << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                                        //cout << approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                                        //if(graphIn[ myNet.sourceVertex ].gate_index == -1){
                                                        //cout << "Oh no, look at this " << graphIn[ myNet.sourceVertex ].instanceName << "\t" << graphIn[ in ].instanceName << endl;
                                                        //}
                                                        if(isValid == true){
                                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[0].z;
                                                                expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[1].z;
#ifdef MORE_PLANE                
                                                                expr3 += approxTerms[2].x * yi[i] + approxTerms[2].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[2].z;
#endif
                                                                
                                                        }
                                                }
                                                
                                                if(flag == 1 && isValid == true){
                                                        //model.add(Si[i] >= expr1  - M + M * Xoi[i][j]);
                                                        //model.add(Si[i] >= expr2  - M + M * Xoi[i][j]);
                                                        model.add(Si[i] >= expr1 - M * (1 - Xoi[i][j]));
                                                        model.add(Si[i] >= expr2 - M * (1 - Xoi[i][j]));
#ifdef MORE_PLANE                
                                                        model.add(Si[i] >= expr3 - M * (1 - Xoi[i][j]));
#endif
                                                        //model.add(Si[i] >= 80.0);
                                                        //model.add(Si[i] >= expr1);
                                                        //model.add(Si[i] >= expr2);
                                                }
                                                
                                                expr1.end();
                                                expr2.end();
                                                expr3.end();
                                                //cout << "Coefficients are " << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                                //cout <<  approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                        }
                                }
                                else if(graphIn[ in ].gateType == 3){
                                        
                                        int flag = 0;
                                        window w0;
                                        bool isValid = true;
                                        IloExpr expr1(env,0);
                                        IloExpr expr2(env,0);
                                        vector<vec> approxTerms;
                                        
                                        inputPin = "ck";
#ifndef RADIUS_BASED 
                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]], inputPin, w0, 
                                                                       0.0, 0.0, 1, isValid);
#else
                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]], inputPin, graphIn[ in ].lut_radius, 
                                                                       0.0, 0.0, 1, isValid);
#endif
                                        
                                        if(graphIn[ myNet.sourceVertex ].gateType == 0){
                                                flag = 1;
                                                inputSlew = sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransFall;
                                                //cout << "instance name is " << graphIn[ myNet.sourceVertex ].instanceName << endl;
                                                //cout << "inputSlew " << inputSlew << endl;
                                                assert(inputSlew == sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransRise);
                                                //assert(inputSlew == 80.0);//does not hold for clk
                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * inputSlew + approxTerms[0].z;
                                                //expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * inputSlew + approxTerms[1].z;
                                        }
                                        else if(graphIn[ myNet.sourceVertex ].gateType == 2 || graphIn[ myNet.sourceVertex ].gateType == 3){
                                                flag = 1;
                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[0].z;
                                                //expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[1].z;
                                        }
                                        
                                        if(flag == 1){
                                                model.add(Si[i] == expr1);
                                                //model.add(Si[i] == expr2);
                                                //model.add(Si[i] >= 80.0);
                                                //model.add(Si[i] >= 80.0);        
                                        }
                                        
                                        expr1.end();
                                        expr2.end();
                                        //cout << "Coefficients are " << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                        //cout <<  approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;        
                                }
                        }
                                                
                        i++;
                }
                
                
                //delay constraint  Di >= Approximated Expression
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                        
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        for(it_pin = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].pins.begin();
                            it_pin != lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].pins.end(); it_pin++)
                        {
                                if(it_pin->name == "o")
                                        continue;
                                
                                inputPin = it_pin->name;
                                for(it_vec = vpPtr->verilogConnectivity.begin(); it_vec != vpPtr->verilogConnectivity.end(); it_vec++)
                                        if(it_vec->netName == graphIn[ in ].mapPinNetDirection[inputPin].first)
                                                break;
                                
                                assert(it_vec != vpPtr->verilogConnectivity.end());
                                netConnectivity myNet = vpPtr->verilogConnectivity[distance(vpPtr->verilogConnectivity.begin(), it_vec)];
                                
                                //cout << "_look " << graphIn[ myNet.sourceVertex ].gate_index << endl;
                                //cout << "_look2 " << graphIn[ myNet.sourceVertex ].instanceName << endl;
                                //cout << "_look3 " << graphIn[ myNet.sourceVertex ].gateType << endl;        
                                
                                if(myNet.sourceVertex == in)
                                        continue;
                                
                                if(graphIn[ myNet.sourceVertex ].gateType == 3 && graphIn[ in ].loopDFFs.find(myNet.sourceVertex) != graphIn[ in ].loopDFFs.end())
                                {
                                        //cout << "Happen once!" << endl;
                                        //continue;
                                }
                                
                                if(graphIn[ in ].gateType == 2){
                                        for(j = 0; j < numOptions; j++){
                                                
                                                int flag = 0;
                                                window w0;
                                                bool isValid = true;
                                                IloExpr expr1(env,0);
                                                IloExpr expr2(env,0);
                                                IloExpr expr3(env,0);
                                                vector<vec> approxTerms;
                                                
                                                if(opt_rd == 0){
#ifndef REDUCED_OPTIONS

#ifndef RADIUS_BASED 
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, w0, 
                                                                                       0.0, 0.0, 0, isValid);
#else
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                       0.0, 0.0, 0, isValid);
#endif

#else
                                                        if(j == graphIn[ in ].candCells.size())
                                                                break;
#ifndef RADIUS_BASED 
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, w0, 
                                                                                       0.0, 0.0, 0, isValid);
#else
                                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                       0.0, 0.0, 0, isValid);
#endif

#endif
                                                        
                                                        //cout << "Instance name is " << graphIn[ in ].instanceName << " Delay" << endl;
                                                        //cout << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                                        //cout << approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                                }
                                                else{
                                                        window w;
                                                        string tmp_name = graphIn[ in ].instanceName + "/" + inputPin;
#ifdef ZOOM_IN
#ifndef REDUCED_OPTIONS
                                                        w = lutZoomin(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], graphIn[ in ].instanceName, inputPin, outputLdArray[i], isValid);
#else
                                                        if(j == graphIn[ in ].candCells.size())
                                                                break;
                                                        w = lutZoomin(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], graphIn[ in ].instanceName, inputPin, outputLdArray[i], isValid);
#endif
#endif
                                                        /*
                                                        if(w.x_width == 0){
                                                                cout << "Check here! Delay" << graphIn[ in ].instanceName << "\t" <<
                                                                        lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].footprint << endl;
                                                                cout << "loading is " << outputLdArray[i] << endl;
                                                                cout << lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]] << endl;
                                                                cin.get();
                                                                }
                                                        */
                                                        
                                                        assert(tpPtr->PinsTiming.find(tmp_name) != tpPtr->PinsTiming.end());
                                                        if(isValid == true){
#ifndef REDUCED_OPTIONS

#ifndef RADIUS_BASED 
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, w, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 0, isValid);
#else
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 0, isValid);
#endif

#else
                                                                if(j == graphIn[ in ].candCells.size())
                                                                        break;
                                                                //cout << "Instance name is " << graphIn[ in ].instanceName << " Delay" << endl;
                                                                //cout << "check here " << max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition) << endl;
                                                                //cout << "check loading " << outputLdArray[i] << endl;
#ifndef RADIUS_BASED                                            
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, w, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 0, isValid);
#else
                                                                approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]], inputPin, graphIn[ in ].lut_radius, 
                                                                                               outputLdArray[i], max(tpPtr->PinsTiming[tmp_name].riseTransition, tpPtr->PinsTiming[tmp_name].fallTransition), 0, isValid);
#endif

#endif
                                                                //if(isValid == true){
                                                                //cout << approxTerms[0].x << "\t" << approxTerms[0].y << "\t" << approxTerms[0].z << endl;
                                                                //cout << approxTerms[1].x << "\t" << approxTerms[1].y << "\t" << approxTerms[1].z << endl;
                                                                //}
                                                        }
                                                }
                                                
                                                assert(graphIn[ myNet.sourceVertex ].gateType != 1);
                                                assert(graphIn[ myNet.sourceVertex ].gate_index != i);
                                                
                                                if(graphIn[ myNet.sourceVertex ].gateType == 0){
                                                        flag = 1;
                                                        inputSlew = sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransFall;
                                                        assert(inputSlew == sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransRise);
                                                        assert(inputSlew == 80.0);
                                                        inputDelay = sdcPtr->inputDelays[graphIn[ myNet.sourceVertex ].instanceName];
                                                        assert(inputDelay == 0.0);
                                                        if(isValid == true){
                                                                //assert(approxTerms.size() == 2);
                                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * inputSlew + approxTerms[0].z ;
                                                                expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * inputSlew + approxTerms[1].z ;
#ifdef MORE_PLANE                
                                                                expr3 += approxTerms[2].x * yi[i] + approxTerms[2].y * inputSlew + approxTerms[2].z;
#endif
                                                        }
                                                }
                                                else if(graphIn[ myNet.sourceVertex ].gateType == 2 || graphIn[ myNet.sourceVertex ].gateType == 3){
                                                        flag = 2;
                                                        if(isValid == true){
                                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[0].z ;
                                                                expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[1].z ;
#ifdef MORE_PLANE                
                                                                expr3 += approxTerms[2].x * yi[i] + approxTerms[2].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[2].z;
#endif
                                                        }
                                                }
                                                
                                                if(flag == 1 && isValid == true){
                                                        //model.add(Di[i] >= inputDelay + 0.1);
#ifndef RELAX_DELAY
                                                        model.add(Di[i] >= inputDelay + expr1 - M * (1 - Xoi[i][j]));
                                                        model.add(Di[i] >= inputDelay + expr2 - M * (1 - Xoi[i][j]));
#ifdef MORE_PLANE                
                                                        model.add(Di[i] >= inputDelay + expr3 - M * (1 - Xoi[i][j]));
#endif
#else
                                                        model.add(Di[i] >= inputDelay + expr1 + DBi[i] - M * (1 - Xoi[i][j]));
                                                        model.add(Di[i] >= inputDelay + expr2 + DBi[i] - M * (1 - Xoi[i][j]));
#ifdef MORE_PLANE                
                                                        model.add(Di[i] >= inputDelay + expr3 + DBi[i] - M * (1 - Xoi[i][j]));
#endif
#endif
                                                }
                                                
                                                if(flag == 2 && isValid == true){
                                                        //model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + 15.0 - M * (1 - Xoi[i][j]));
#ifndef RELAX_DELAY
                                                        model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr1 - M * (1 - Xoi[i][j]));
                                                        model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr2 - M * (1 - Xoi[i][j]));
#ifdef MORE_PLANE
                                                        model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr3 - M * (1 - Xoi[i][j]));
#endif
#else
                                                        model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr1 + DBi[i] - M * (1 - Xoi[i][j]));
                                                        model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr2 + DBi[i] - M * (1 - Xoi[i][j]));
#ifdef MORE_PLANE
                                                        model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr3 + DBi[i] - M * (1 - Xoi[i][j]));
#endif
#endif
                                                        //model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index]);
                                                        //model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index]);
                                                        
                                                }
                                                
                                                expr1.end();
                                                expr2.end();
                                                expr3.end();
                                        }
                                }
                                
                                if(graphIn[ in ].gateType == 3){
                                        
                                        int flag = 0;
                                        window w0;
                                        bool isValid = true;
                                        IloExpr expr1(env,0);
                                        IloExpr expr2(env,0);
                                        vector<vec> approxTerms;
                                        inputPin = "ck";
#ifndef RADIUS_BASED
                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]], inputPin, w0, 
                                                                       0.0, 0.0, 0, isValid);
#else
                                        approxTerms = lutApproximation(lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]], inputPin, graphIn[ in ].lut_radius, 
                                                                       0.0, 0.0, 0, isValid);
#endif
                                        assert(graphIn[ myNet.sourceVertex ].gateType != 1);
                                        if(graphIn[ myNet.sourceVertex ].gateType == 0){
                                                flag = 1;
                                                inputSlew = sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransFall;
                                                assert(inputSlew == sdcPtr->inputDrivers[graphIn[ myNet.sourceVertex ].instanceName].inputTransRise);
                                                //assert(inputSlew == 80.0);
                                                inputDelay = sdcPtr->inputDelays[graphIn[ myNet.sourceVertex ].instanceName];
                                                assert(inputDelay == 0.0);
                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * inputSlew + approxTerms[0].z ;
                                                //expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * inputSlew + approxTerms[1].z ;
                                        }
                                        else if(graphIn[ myNet.sourceVertex ].gateType == 2 || graphIn[ myNet.sourceVertex ].gateType == 3){
                                                flag = 2;
                                                expr1 += approxTerms[0].x * yi[i] + approxTerms[0].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[0].z ;
                                                //expr2 += approxTerms[1].x * yi[i] + approxTerms[1].y * Si[graphIn[ myNet.sourceVertex ].gate_index] + approxTerms[1].z ;
                                        }
                                        
                                        if(flag == 1){
                                                //model.add(Di[i] >= inputDelay + expr1);
                                                //model.add(Di[i] >= inputDelay + expr2);
                                                model.add(Di[i] == expr1);
                                                //model.add(Di[i] == expr2);
                                                //model.add(Di[i] == 0.0);
                                                //model.add(Di[i] == 0.0);
                                        }
                                        
                                        if(flag == 2){
                                                //model.add(Di[i] == 0.0);
                                                //model.add(Di[i] == 0.0);
                                                model.add(Di[i] == expr1);
                                                //model.add(Di[i] == expr2);
                                                //model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr1);
                                                //model.add(Di[i] >= Di[graphIn[ myNet.sourceVertex ].gate_index] + expr2);
                                        }
                                        
                                        expr1.end();
                                        expr2.end();
                                }
                        }
                        
                        i++;
                }
                
                
                //bound arrival time of DFF and PO
                i = 0;
                cout << "Clock period is " << sdcPtr->clk_info.period << endl;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
                {       
                        vertex_descriptor in = (*vi);
                        //cout << "the name of the instance is " << graphIn[ in ].instanceName << endl;
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        //cout << "the name of the instance is " << graphIn[ in ].instanceName << endl;
                        //assert(graphIn[ in ].listOfCells.size() == numOptions);
                        
                        if(graphIn[ in ].gateType == 2){
                                for (boost::tie(adj_vi, adj_vi_end) = boost::adjacent_vertices(in, graphIn); adj_vi != adj_vi_end ; ++adj_vi){
                                        if(graphIn[*adj_vi].gateType == 1 || graphIn[*adj_vi].gateType == 3)
                                                //if(graphIn[*adj_vi].gateType == 3)
                                        {
                                                model.add(Di[i] <= sdcPtr->clk_info.period + DBi[i]);
                                                break;
                                        }
                                }
                        }
                        
                        i++;
                }
                
                
                //note that we don't consider the output delay since it is
                //pre-assumed to be 0.0
                
                //bound slew
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                        
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        model.add(Si[graphIn[ in ].gate_index] <= 300.0 + SBi[i]);
                        i++;
                }
                
                /*
                //bound fanout capacitance
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                        
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
                        IloExpr expr1(env,0);
                        if(graphIn[ in ].gateType == 2){
                                for(j = 0; j < numOptions; j++){
#ifndef REDUCED_OPTIONS
                                        for(it_pin = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].pins.begin(); it_pin != lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].pins.end(); it_pin++)
                                        {
                                                if(it_pin->name == "o"){
                                                        expr1 += it_pin->maxCapacitance * Xoi[i][j];
                                                        break;
                                                }
                                        }
#else
                                        if(j == graphIn[ in ].candCells.size())
                                                break;
                                        
                                        for(it_pin = lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].pins.begin(); it_pin != lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].pins.end(); it_pin++)
                                        {
                                                if(it_pin->name == "o"){
                                                        expr1 += it_pin->maxCapacitance * Xoi[i][j];
                                                        break;
                                                }
                                        }
#endif
                                }
                        }
                        
                        if(graphIn[ in ].gateType == 2)
                                model.add(yi[graphIn[ in ].gate_index] <= expr1);
                        
                        expr1.end();
                        i++;
                }
                */
                
                //----------------------------------------------------solve and optimize-----------------------------------------------------
                IloCplex cp(model);
                cp.setParam(cp.EpGap,0.15);
                cp.setParam(cp.TiLim, 300);
                cp.setParam(cp.TreLim,6e7);
                cp.setParam(cp.MemoryEmphasis,true);
                cp.extract(model);
                //cp.exportModel("gt.sav");
                //cp.exportModel("gt.lp");
                cp.solve();
                
                ofstream logfile("my.log");
                cp.setOut(logfile);		  
                
                //----------------------------------------------------print out solution-----------------------------------------------------
                cout << std::endl << "Best Optimal value: " << cp.getBestObjValue() << std::endl;		    
                cout << std::endl << "Optimal value: " << cp.getValue(obj) << std::endl;
                cout << cp.getStatus() << std::endl;

                if(cp.getStatus() == IloAlgorithm::Infeasible)
                {
                        for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi)
                        {
                                vertex_descriptor in = (*vi);
                                
                                if(graphIn[ in ].gateType == 2){
                                        graphIn[ in ].opt_radius = 2;
                                        graphIn[ in ].lut_radius = 3;
                                }
                        }
                }
                int ncols = cp.getNcols();
              
                string solution_file = "solution_";
                stringstream ss;
                ss << opt_rd;
                solution_file += ss.str();
                
                cout<<"Cplex output file name in this round is "<<solution_file<<endl;
                ofstream out(solution_file.c_str());
                
                out<<"Gate sizes are "<<endl;
                i = 0;
                for (boost::tie(vi, vi_end) = boost::vertices(graphIn) ; vi != vi_end ; ++vi){
                        
                        vertex_descriptor in = (*vi);
                        if(graphIn[ in ].gateType == 0 || graphIn[ in ].gateType == 1)
                                continue;
                        
#ifdef LIN_RELAX
                        if(graphIn[ in ].gateType == 2){
                                double temp = 0.0; int best_node;
                                for(j = 0; j < numOptions; j++){
                                        if(cp.getValue(Xoi[i][j]) > temp){
                                                temp = cp.getValue(Xoi[i][j]);
                                                best_node = j;
                                        }
                                }
                                
                                j = best_node;
#ifndef REDUCED_OPTIONS
                                graphIn[ in ].cellName = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].name;//set cell size
                                out << "Gate: " << graphIn[ in ].instanceName << "\tSize: " << j << "\t" << lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].name << endl;
#else
                                //cout << "Look at here" << j << endl;
                                //cout << graphIn[ in ].candCells[j] << "\t" << lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].name << endl;
                                if(j == graphIn[ in ].candCells.size())
                                        break;
                                        
                                graphIn[ in ].cellName = lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].name;//set cell size
                                out << "Gate: " << graphIn[ in ].instanceName << "\tSize: " << j << "\t" << lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].name << endl;
#endif
                                break;
                        }
                        
#else
                        if(graphIn[ in ].gateType == 2){
                                for(j = 0; j < numOptions; j++){
                                        if(cp.getValue(Xoi[i][j]) == 1)
                                        {
#ifndef REDUCED_OPTIONS
                                                graphIn[ in ].cellName = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].name;//set cell size
                                                out << "Gate: " << graphIn[ in ].instanceName << "\tSize: " << j << "\t" << lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[j]].name << endl;
#else
                                                //cout << "Look at here" << j << endl;
                                                //cout << graphIn[ in ].candCells[j] << "\t" << lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].name << endl;
                                                if(j == graphIn[ in ].candCells.size())
                                                        break;
                                                
                                                graphIn[ in ].cellName = lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].name;//set cell size
                                                out << "Gate: " << graphIn[ in ].instanceName << "\tSize: " << j << "\t" << lpPtr->mapStandardLibraryCells[graphIn[ in ].candCells[j]].name << endl;
#endif
                                                break;
                                        }
                                }
                        }
#endif

                        if(graphIn[ in ].gateType == 3)
                        {
                                graphIn[ in ].cellName = lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].name;//set cell size
                                out << "Gate: " << graphIn[ in ].instanceName << "\tSize: " << 0 << "\t" << lpPtr->mapStandardLibraryCells[graphIn[ in ].listOfCells[0]].name << endl;
                        }
                        
                        outputLdArray[i] = cp.getValue(yi[i]);
#ifndef RELAX_DELAY
                        out << "Delay: " << cp.getValue(Di[i]) << "\tSlew: " << cp.getValue(Si[i]) << "\tLoading: " << cp.getValue(yi[i]) << endl;
#else
                        out << "Delay: " << cp.getValue(Di[i]) << "\tDelay Budget: " << cp.getValue(DBi[i]) << "\tSlew: " << cp.getValue(Si[i]) << "\tLoading: " << cp.getValue(yi[i]) << endl;
#endif
                        i++;
                }
                
                out.close();
                cp.end();
                model.end();
        }
        catch (IloException& ex) {
                env.out() << "Error: " << ex << std::endl;
        }
        
        env.end();        
        return 0;
}


int main (int argc, char* argv[]) 
{
        if (argc != 3) {
		cout << "Usage: " << argv[0] << " benchmark_name library_file" << endl ;
		exit(0) ;
	}
        
	string contest_dir, benchmark_dir ;	
	contest_dir = argv[1]; 
	benchmark_dir = argv[2];
        cout<<"Benchmark directory is "<<benchmark_dir<<endl;
        
        clock_t startBenchmarkReading = clock() ;
        
	clock_t startReadStandardLibraryCell = clock() ;
	// 1. Read Standard Library Cell
	//------------------------------------------------------------------------------------
	string standardLibFile = contest_dir+"/lib/contest.lib" ;
	cout << "Start reading standard library cell ( " <<  standardLibFile << " )" << endl ;
	ExtendedLibParser lp (standardLibFile) ;
	lp.readStandardLibraryCell () ;
        lpPtr = &lp;
        //cout<<"Default Max Transition is " << lpPtr->default_max_transition <<endl;
        
        
        // 2. Read Sdc File
        string sdcFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".sdc" ;
        ExtendedSdcParser sdcp(sdcFile);
        sdcp.readSdcFile();
        sdcPtr = &sdcp;
	//------------------------------------------------------------------------------------
	clock_t endReadStandardLibraryCell = clock() ;
	
	
	clock_t startReadSpef = clock() ;
	// 3. Read Parasitic Load
	//-----------------------------------------------------------------------------------
	string spefFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".spef" ;
	cout << "Start reading parasitic load from " << spefFile << endl ;
	ExtendedSpefParser sp (spefFile) ;
	sp.readSpefFile () ;
	//-----------------------------------------------------------------------------------
	clock_t endReadSpef = clock() ;

	
	// 4. Initialize the circuit graph
	//-----------------------------------------------------------------------------------
	cout << "The graph is initialized..." << endl;
	CircuitGraph inputCircuit ;
        graphInPtr = &inputCircuit;
	//-----------------------------------------------------------------------------------
	
	clock_t startReadVerilog = clock() ;
	// 5. Read Verilog File
	//-----------------------------------------------------------------------------------
	string verilogFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".v" ;
	cout << "Start reading verilog file " << verilogFile << endl ;
	ExtendedVerilogParser vp (verilogFile) ;
        vpPtr = &vp;
	vp.readVerilogFile(inputCircuit, lp.mapStandardLibraryCells) ;
	cout << "End reading verilog file..." << endl ;
	//-----------------------------------------------------------------------------------
	clock_t endReadVerilog = clock() ;
        

	// 6. Update edges in graph
	//-----------------------------------------------------------------------------------
	inputCircuit.updateConnectivity(vp.verilogConnectivity, sp.mapNetParasiticLoads) ;
	//-----------------------------------------------------------------------------------
	cout << "initially connectivity size is " << vp.verilogConnectivity.size() << endl;
	//inputCircuit.printGateProperty < string,  gateProperty> (&gateProperty::cellName) ;
        //double leakageBefore = inputCircuit.accumTotalLeakage(lp.mapStandardLibraryCells) ;
        
	cout << "The graph is ready for optimization..." << endl ;
        cout.precision(3) ;
	clock_t startOptimization = clock() ;
        
        /*Amir's part......................*/
        //6. Read .sdc file                                                                                                      
        // -----------------------------------------------------------------------------------
        //sdcp.updateOutputCap(inputCircuit) ;
        //sdcp.updateInputTime (inputCircuit, lp) ;

        //----------------------------------------------------------------------------------
        // Find the range of Output Capacitance for each gate and store
        // that in a file                                            
        //----------------------------------------------------------------------------------------------                          
        string capFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + "_capRange.txt" ;
        ofstream outCapacitance(capFile.c_str()) ;
        //StoreOutCapacitance storeFunc ; 
        //inputCircuit.updateMinMaxOutputCap (lp, storeFunc, outCapacitance, true) ;
        //----------------------------------------------------------------------------------------------        
        clock_t endBenchmarkReading = clock();
        
        /*......................Min's part 10/23/2012......................*/
        boost::graph_traits<CircuitGraph>::vertex_iterator vi, vi_end ;
        
        int i = 0;
        long count = 0;
        long count_dff = 0;
        effGateCnt = boost::num_vertices(inputCircuit);
        for (boost::tie(vi, vi_end) = boost::vertices(inputCircuit) ; vi != vi_end ; ++vi)
        {
                vertex_descriptor in = (*vi);
                if(inputCircuit[ in ].gateType == 0 || inputCircuit[ in ].gateType == 1){
                        inputCircuit[ in ].gate_index = -1;
                        count++;
                }
                
                if(inputCircuit[ in ].gateType == 2){
                        inputCircuit[ in ].wasFixed = false;
                        inputCircuit[ in ].isFixed = false;
                        inputCircuit[ in ].gate_index = i;
                        inputCircuit[ in ].opt_radius = 1;
                        inputCircuit[ in ].lut_radius = 1;
                        i++;
                }
                        
                if(inputCircuit[ in ].gateType == 3){
                        inputCircuit[ in ].gate_index = i;
                        i++;
                        count_dff++;
                }
        }
        effGateCnt -= count;

        //topological sort
        topoSort(inputCircuit);
        
        double* outputLdArray = new double[effGateCnt];
        memset(outputLdArray, -1.0, sizeof(double) * effGateCnt);
        
        string timingFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".timing" ;
        ExtendedTimingParser tp (timingFile) ;
        tpPtr = &tp;
        
        opt_rd = 0;
        int try_time = 5;
        largestNumOption = 0;
        //Optimization op (contest_dir, benchmark_dir, 3, &minLeakage) ;
        Optimization op (contest_dir, benchmark_dir, 3, NULL) ;
        double leakageBefore = inputCircuit.accumTotalLeakage(lp.mapStandardLibraryCells) ;        

#ifdef ENA_PPRO
        string tempSizerFileName = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir;
        stringstream ss;
        ss << 2;
        tempSizerFileName += "_" + ss.str() + ".sizes";
        
        vector< pair<string, string> > instSizePair;
        instSizePair = op.readSizes(tempSizerFileName.c_str());
        
        int i = 0;
        for (tie(vi, vi_end) = boost::vertices(inputCircuit) ; vi != vi_end ; ++vi)
	{
		if (inputCircuit[(*vi)].gateType == Combinational || inputCircuit[(*vi)].gateType == Sequential){
                        inputCircuit[(*vi)].cellName = instSizePair[i].second;
                        i++;
                }
        }
        
        std::string violationStr = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + "_violations.txt";
        ofstream violationFile(violationStr.c_str()) ;
        violationFile << "TNS\t" << "TNS Decrease%\t"
                      << "TotalSlew\t" << "TotalCap\t" << "TotalViolations\t" 
                      << "Leakage\t" << "Leakage Increas%\t"
                      << "Best TNS\t" << "Best Leakage\t"
                      << "Best Cap Viol\t" << "Best Slew Viol\t"
                      << "Changed Cells\t" << "Changed Cells %\t"
                      << "RunTime\t" << endl ;
        
        bool noSlew = false; bool noSlack = false; bool noCap = false;
        while(!(noSlew && noSlack && noCap)){
                cout<< "Post-processing round " << opt_rd << endl;
                
                double firstTNS = 0.0 ;
                double firstLeakage = 0.0 ;
                double slackViolation; double slewViolation;
                double capViolation; double leakage;        
                
                cout << "Make the sizer file... " << endl ;
                stringstream ss;
                ss << opt_rd;
                //ss.str() += "_pp" ;
                op.buildSizerFile (inputCircuit, false) ;
                //op.buildSizerFile (inputCircuit, false, ss.str()) ;
                cout << "Call timer..." << endl ;
                op.callTimer() ;
                cout << "Finished!!!" << endl ;
                
                tp.updateTimingFile(timingFile);
                
                cout << "From check script ... " << endl;
                violation_ctrl MyVc(contest_dir, benchmark_dir, standardLibFile);        
                
                noSlew = false ; noSlack = false ; noCap = false ;			
                slackViolation = 0.0 ; slewViolation = 0.0 ;
                capViolation = 0.0 ; leakage = 0.0 ;
		
                MyVc.check_violations(firstTNS, firstLeakage, violationFile, noSlew, noSlack, noCap,
                                      slewViolation, slackViolation, capViolation, leakage) ;
                
                //cout << "no slack?" << noSlack << endl;
                double cur_worst = 0.0;
                vertex_descriptor cur;
                for (tie(vi, vi_end) = boost::vertices(inputCircuit) ; vi != vi_end ; ++vi)
                {
                        vertex_descriptor in = (*vi) ;
                        if(inputCircuit[ in ].gateType == 2){
                                double temp = nodeVioCheck(inputCircuit, in);
                                if(cur_worst < temp){
                                        cur_worst = temp;
                                        cur = in;
                                }
                        }
                }
                
                if(cur_worst > 0.0001)
                        postProcessing(inputCircuit, cur);
                else
                        cout << "Post Processing Done!" << endl;
                
                opt_rd++;
                if(opt_rd == 20)
                        break;
        }
        
#else
        while(opt_rd < try_time){
        clock_t startUpSizing = clock();
        if(opt_rd > 0){
                solver(inputCircuit, outputLdArray, vpPtr, sdcPtr, lpPtr);
                for (boost::tie(vi, vi_end) = boost::vertices(inputCircuit) ; vi != vi_end ; ++vi)
                {
                        vertex_descriptor in = (*vi);
                        inputCircuit[ in ].wasFixed = inputCircuit[ in ].isFixed;
                }
        }
        clock_t endUpSizing = clock();
        //return 1;
        
        
        /*Amir's part......................*/
        
	// 7. Initialize optimization,
        clock_t startCallTimer = clock();
        //if(opt_rd > 0){
        //cout << "Call updater function..." << endl ;
        //op.updateCellType (inputCircuit) ;
        //}
        cout << "Make the sizer file... " << endl ;
        stringstream ss;
        ss << opt_rd;
        //op.buildSizerFile (inputCircuit, false) ;
        op.buildSizerFile (inputCircuit, false, ss.str()) ;
        cout << "Call timer..." << endl ;
        op.callTimer() ;
        cout << "Finished!!!" << endl ;
        
        //Min tp originally here
        //if(opt_rd == 0){
        //tp.readTimingFile();
        //}
        //else
        tp.updateTimingFile(timingFile);
        
        clock_t endCallTimer = clock();
        
        cout.precision(5) ;
        cout << "Sizer runtime info" << endl;
        cout << (double)(endUpSizing - startUpSizing) / (double)CLOCKS_PER_SEC << "\t";
        cout << (double)(endCallTimer - startCallTimer) / (double)CLOCKS_PER_SEC << endl;
        
	double timeStandard = (double)(endReadStandardLibraryCell - startReadStandardLibraryCell) / (double)CLOCKS_PER_SEC ;
	double timeSpef = (double)(endReadSpef - startReadSpef) / (double)CLOCKS_PER_SEC ;
	double timeVerilog = (double)(endReadVerilog - startReadVerilog) / (double)CLOCKS_PER_SEC ;
	
        cout << "--------------Run time---------------" << endl ;
	cout << "Reading Standard Library Cell: " <<  timeStandard << endl;
	cout << "Reading SPEF: " <<  timeSpef << endl;
	cout << "Reading Verilog: " << timeVerilog << endl;
	cout << "Total: " << timeStandard + timeSpef + timeVerilog << endl ;
        cout << "Reading benchmark: " << (double)(endBenchmarkReading - startBenchmarkReading) / (double)CLOCKS_PER_SEC <<endl;
	cout << "-------------------------------------" << endl ;
        
	
	//------------------------------------------------------------------------------------Amir---------------------------------------------------------------------	
        std::string violationStr = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + "_violations.txt";
        ofstream violationFile(violationStr.c_str()) ;
	
        violationFile << "TNS\t" << "TNS Decrease%\t"
                      << "TotalSlew\t" << "TotalCap\t" << "TotalViolations\t" 
                      << "Leakage\t" << "Leakage Increas%\t"
                      << "Best TNS\t" << "Best Leakage\t"
                      << "Best Cap Viol\t" << "Best Slew Viol\t"
                      << "Changed Cells\t" << "Changed Cells %\t"
                      << "RunTime\t" << endl ;
        
        double firstTNS = 0.0 ;
        double firstLeakage = 0.0 ;
	//------------------------------------------------------------------------------------Amir---------------------------------------------------------------------
	
        
        //------------------------------------------------------------------------------------Min---------------------------------------------------------------------
        int count = 0;
        bool write_to_file = true;
        string resultFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".result2";
        ofstream out(resultFile.c_str());
        cout << "Result file name is "<< resultFile.c_str() <<endl;
        
        string checkVioFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".checkvio";
        string sizeFile = contest_dir + "/" + benchmark_dir + "/" + benchmark_dir + ".sizes";
        
        bool noSlew; bool noSlack; bool noCap;			
        double slackViolation; double slewViolation;
        double capViolation; double leakage;        
        
        cout << "From check script ... " << endl;
        violation_ctrl MyVc(contest_dir, benchmark_dir, standardLibFile);        
        
        noSlew = false ; noSlack = false ; noCap = false ;			
        slackViolation = 0.0 ; slewViolation = 0.0 ;
        capViolation = 0.0 ; leakage = 0.0 ;
		
        MyVc.check_violations(firstTNS, firstLeakage, violationFile, noSlew, noSlack, noCap,
                              slewViolation, slackViolation, capViolation, leakage) ;
        
        cout << endl;
        cout << "From my script ... " << endl;
        double TViolFromMine = violationCheck(inputCircuit, !write_to_file, outputLdArray);
                
        
        //finished and reporting
        cout.precision(3) ;
	cout << "The leakage power is (before optimization): " << leakageBefore << endl ;
	cout << "The leakage power is (after optimization): " << inputCircuit.accumTotalLeakage(lp.mapStandardLibraryCells) << endl ;	
        
        opt_rd++;
        }
#endif
        return 0;
}
